From 6ba94ce79c8477758dd440d3563ffd4dd88883df Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 18:11:05 +0200 Subject: kern/rtmutex: fix unlock Commit 42b089048fc0d3e67fa10cb411767afa161c7222 was apparently incomplete. --- kern/rtmutex.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kern/rtmutex.h b/kern/rtmutex.h index e5cb5e5..f64274d 100644 --- a/kern/rtmutex.h +++ b/kern/rtmutex.h @@ -100,11 +100,9 @@ rtmutex_unlock(struct rtmutex *rtmutex) prev_owner = rtmutex_unlock_fast(rtmutex); - if (prev_owner & RTMUTEX_CONTENDED) { - return; + if (unlikely(prev_owner & RTMUTEX_CONTENDED)) { + rtmutex_unlock_slow(rtmutex); } - - rtmutex_unlock_slow(rtmutex); } #endif /* _KERN_RTMUTEX_H */ -- cgit v1.2.3 From d78a948f0a1d8b7b3385944c736d7cffd8ca67a7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 21:52:54 +0200 Subject: kern/printk: rename to printf The printk functions are close enough to the printf ones to bear the same names. --- Makefrag.am | 4 +-- arch/x86/machine/acpimp.c | 14 ++++----- arch/x86/machine/biosmem.c | 16 +++++----- arch/x86/machine/boot.c | 6 ++-- arch/x86/machine/cpu.c | 10 +++--- arch/x86/machine/lapic.c | 8 ++--- arch/x86/machine/pmap.c | 2 +- arch/x86/machine/strace.c | 28 ++++++++--------- arch/x86/machine/trap.c | 16 +++++----- arch/x86/machine/trap.h | 4 +-- doc/intro.9.txt | 2 +- doc/style.9.txt | 2 +- kern/kmem.c | 10 +++--- kern/llsync.c | 4 +-- kern/panic.c | 8 ++--- kern/percpu.c | 10 +++--- kern/printf.c | 73 +++++++++++++++++++++++++++++++++++++++++++ kern/printf.h | 38 ++++++++++++++++++++++ kern/printk.c | 73 ------------------------------------------- kern/printk.h | 38 ---------------------- kern/sref.c | 2 +- kern/syscnt.c | 6 ++-- kern/task.c | 6 ++-- kern/work.c | 6 ++-- test/test_llsync_defer.c | 8 ++--- test/test_mutex_pi.c | 8 ++--- test/test_pmap_update_mp.c | 14 ++++----- test/test_sref_dirty_zeroes.c | 2 +- test/test_sref_noref.c | 16 +++++----- test/test_sref_weakref.c | 4 +-- test/test_vm_page_fill.c | 2 +- test/test_xcall.c | 8 ++--- vm/vm_map.c | 8 ++--- vm/vm_page.c | 10 +++--- 34 files changed, 233 insertions(+), 233 deletions(-) create mode 100644 kern/printf.c create mode 100644 kern/printf.h delete mode 100644 kern/printk.c delete mode 100644 kern/printk.h diff --git a/Makefrag.am b/Makefrag.am index 1036aff..b7013c2 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -46,8 +46,8 @@ x15_SOURCES += \ kern/plist.c \ kern/plist.h \ kern/plist_types.h \ - kern/printk.c \ - kern/printk.h \ + kern/printf.c \ + kern/printf.h \ kern/rbtree.c \ kern/rbtree.h \ kern/rbtree_i.h \ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 2c3dc6c..527b305 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -159,7 +159,7 @@ acpimp_register_table(struct acpimp_sdth *table) for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) if (strcmp(sig, acpimp_table_addrs[i].sig) == 0) { if (acpimp_table_addrs[i].table != NULL) { - printk("acpimp: warning: table %s ignored:" + printf("acpimp: warning: table %s ignored:" " already registered\n", sig); return; } @@ -168,7 +168,7 @@ acpimp_register_table(struct acpimp_sdth *table) return; } - printk("acpimp: warning: table '%s' ignored: unknown table\n", sig); + printf("acpimp: warning: table '%s' ignored: unknown table\n", sig); } static struct acpimp_sdth * __init @@ -191,7 +191,7 @@ acpimp_check_tables(void) for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) if (acpimp_table_addrs[i].table == NULL) { - printk("acpimp: error: table %s missing\n", + printf("acpimp: error: table %s missing\n", acpimp_table_addrs[i].sig); return -1; } @@ -325,7 +325,7 @@ acpimp_find_rsdp(struct acpimp_rsdp *rsdp) return 0; } - printk("acpimp: unable to find root system description pointer\n"); + printf("acpimp: unable to find root system description pointer\n"); return -1; } @@ -336,7 +336,7 @@ acpimp_info(void) rsdt = acpimp_lookup_table("RSDT"); assert(rsdt != NULL); - printk("acpimp: revision: %u, oem: %.*s\n", rsdt->revision, + printf("acpimp: revision: %u, oem: %.*s\n", rsdt->revision, (int)sizeof(rsdt->oem_id), rsdt->oem_id); } @@ -375,7 +375,7 @@ acpimp_copy_table(uint32_t addr) char sig[ACPIMP_SIG_SIZE]; acpimp_table_sig(table, sig); - printk("acpimp: table %s: invalid checksum\n", sig); + printf("acpimp: table %s: invalid checksum\n", sig); copy = NULL; goto out; } diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index fecf67a..060be44 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -224,7 +224,7 @@ biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end) } #if DEBUG - printk("biosmem: unregister boot data: %llx:%llx\n", + 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 */ @@ -776,16 +776,16 @@ biosmem_map_show(void) { const struct biosmem_map_entry *entry, *end; - printk("biosmem: physical memory map:\n"); + printf("biosmem: physical memory map:\n"); for (entry = biosmem_map, end = entry + biosmem_map_size; entry < end; entry++) - printk("biosmem: %018llx:%018llx, %s\n", entry->base_addr, + printf("biosmem: %018llx:%018llx, %s\n", entry->base_addr, entry->base_addr + entry->length, biosmem_type_desc(entry->type)); - printk("biosmem: heap: %llx:%llx\n", + printf("biosmem: heap: %llx:%llx\n", (unsigned long long)biosmem_heap_start, (unsigned long long)biosmem_heap_end); } @@ -806,12 +806,12 @@ biosmem_load_zone(struct biosmem_zone *zone, uint64_t max_phys_end) if (phys_end > max_phys_end) { if (max_phys_end <= phys_start) { - printk("biosmem: warning: zone %s physically unreachable, " + printf("biosmem: warning: zone %s physically unreachable, " "not loaded\n", vm_page_zone_name(zone_index)); return; } - printk("biosmem: warning: zone %s truncated to %#llx\n", + printf("biosmem: warning: zone %s truncated to %#llx\n", vm_page_zone_name(zone_index), (unsigned long long)max_phys_end); phys_end = max_phys_end; } @@ -889,7 +889,7 @@ biosmem_free_usable_range(phys_addr_t start, phys_addr_t end) struct vm_page *page; #if DEBUG - printk("biosmem: release to vm_page: %llx:%llx (%lluk)\n", + 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 diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index e14e2fc..e787901 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -54,7 +54,7 @@ #include #include #include -#include +#include #include #include #include @@ -307,7 +307,7 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) static void __init boot_show_version(void) { - printk(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION + printf(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION #ifdef X15_X86_PAE " PAE" #endif /* X15_X86_PAE */ @@ -453,7 +453,7 @@ boot_main(void) cpu_setup(); thread_bootstrap(); cga_setup(); - printk_setup(); + printf_setup(); boot_show_version(); pmap_bootstrap(); sref_bootstrap(); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 9bb593d..1fd3fa8 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -543,16 +543,16 @@ cpu_check(const struct cpu *cpu) void cpu_info(const struct cpu *cpu) { - printk("cpu%u: %s, type %u, family %u, model %u, stepping %u\n", + printf("cpu%u: %s, type %u, family %u, model %u, stepping %u\n", cpu->id, cpu->vendor_id, cpu->type, cpu->family, cpu->model, cpu->stepping); if (strlen(cpu->model_name) > 0) { - printk("cpu%u: %s\n", cpu->id, cpu->model_name); + printf("cpu%u: %s\n", cpu->id, cpu->model_name); } if ((cpu->phys_addr_width != 0) && (cpu->virt_addr_width != 0)) { - printk("cpu%u: address widths: physical: %hu, virtual: %hu\n", + printf("cpu%u: address widths: physical: %hu, virtual: %hu\n", cpu->id, cpu->phys_addr_width, cpu->virt_addr_width); } } @@ -597,7 +597,7 @@ cpu_mp_probe(void) panic("cpu: ACPI required to initialize local APIC"); } - printk("cpu: %u processor(s) configured\n", cpu_count()); + printf("cpu: %u processor(s) configured\n", cpu_count()); } void __init diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index c0d10e5..90886c7 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -211,7 +211,7 @@ lapic_setup_timer(void) cpu_delay(LAPIC_TIMER_CAL_DELAY); c2 = lapic_read(&lapic_map->timer_ccr); lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY); - printk("lapic: bus frequency: %u.%02u MHz\n", lapic_bus_freq / 1000000, + printf("lapic: bus frequency: %u.%02u MHz\n", lapic_bus_freq / 1000000, lapic_bus_freq % 1000000); lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); } @@ -343,7 +343,7 @@ lapic_error_intr(struct trap_frame *frame) (void)frame; esr = lapic_read(&lapic_map->esr); - printk("lapic: error on cpu%u: esr:%08x\n", cpu_id(), esr); + printf("lapic: error on cpu%u: esr:%08x\n", cpu_id(), esr); lapic_write(&lapic_map->esr, 0); lapic_eoi(); } @@ -352,7 +352,7 @@ void lapic_spurious_intr(struct trap_frame *frame) { (void)frame; - printk("lapic: warning: spurious interrupt\n"); + printf("lapic: warning: spurious interrupt\n"); /* No EOI for this interrupt */ } diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index a7c4961..63a2909 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -1162,7 +1162,7 @@ pmap_enter_local(struct pmap *pmap, uintptr_t va, phys_addr_t pa, page = vm_page_alloc(0, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_PMAP); if (page == NULL) { - printk("pmap: warning: page table page allocation failure\n"); + printf("pmap: warning: page table page allocation failure\n"); return ERROR_NOMEM; } diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index e40b364..89f8569 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -74,9 +74,9 @@ strace_show_one(unsigned int index, unsigned long ip) name = strace_lookup(ip, &offset, &size); if (name == NULL) { - printk("strace: #%u [" STRACE_ADDR_FORMAT "]\n", index, ip); + printf("strace: #%u [" STRACE_ADDR_FORMAT "]\n", index, ip); } else { - printk("strace: #%u [" STRACE_ADDR_FORMAT "] %s+%#lx/%#lx\n", + printf("strace: #%u [" STRACE_ADDR_FORMAT "] %s+%#lx/%#lx\n", index, ip, name, (unsigned long)offset, (unsigned long)size); } } @@ -89,7 +89,7 @@ strace_show(unsigned long ip, unsigned long bp) unsigned int i; int error; - printk("strace: stack trace:\n"); + printf("strace: stack trace:\n"); strace_show_one(0, ip); i = 1; @@ -103,7 +103,7 @@ strace_show(unsigned long ip, unsigned long bp) error = pmap_kextract((uintptr_t)&frame[1], &pa); if (error) { - printk("strace: unmapped return address at %p\n", &frame[1]); + printf("strace: unmapped return address at %p\n", &frame[1]); break; } @@ -111,7 +111,7 @@ strace_show(unsigned long ip, unsigned long bp) error = pmap_kextract((uintptr_t)frame, &pa); if (error) { - printk("strace: unmapped frame address at %p\n", frame); + printf("strace: unmapped frame address at %p\n", frame); break; } @@ -119,7 +119,7 @@ strace_show(unsigned long ip, unsigned long bp) frame = frame[0]; } - printk("strace: end of trace\n"); + printf("strace: end of trace\n"); } static void * __init @@ -133,14 +133,14 @@ strace_copy_section(const struct elf_shdr *shdr) src = vm_kmem_map_pa(shdr->addr, shdr->size, &map_addr, &map_size); if (src == NULL) { - printk("strace: unable to map section\n"); + printf("strace: unable to map section\n"); goto error_map; } copy = kmem_alloc(shdr->size); if (copy == NULL) { - printk("strace: unable to allocate section copy\n"); + printf("strace: unable to allocate section copy\n"); goto error_copy; } @@ -191,12 +191,12 @@ strace_setup(const struct multiboot_raw_info *mbi) table = vm_kmem_map_pa(mbi->shdr_addr, size, &map_addr, &map_size); if (table == NULL) { - printk("strace: unable to map section headers table\n"); + printf("strace: unable to map section headers table\n"); goto no_syms; } if (mbi->shdr_strndx >= mbi->shdr_num) { - printk("strace: invalid section names index\n"); + printf("strace: invalid section names index\n"); goto error_shstrndx; } @@ -205,21 +205,21 @@ strace_setup(const struct multiboot_raw_info *mbi) &shstrtab_map_addr, &shstrtab_map_size); if (shstrtab == NULL) { - printk("strace: unable to map section names\n"); + printf("strace: unable to map section names\n"); goto error_shstrtab; } symtab_hdr = strace_lookup_section(mbi, table, shstrtab, ".symtab"); if (symtab_hdr == NULL) { - printk("strace: unable to find symbol table\n"); + printf("strace: unable to find symbol table\n"); goto error_symtab_lookup; } strtab_hdr = strace_lookup_section(mbi, table, shstrtab, ".strtab"); if (strtab_hdr == NULL) { - printk("strace: unable to find symbol string table\n"); + printf("strace: unable to find symbol string table\n"); goto error_strtab_lookup; } diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 5aeb63c..2d25ce9 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -112,7 +112,7 @@ trap_show_thread(void) struct thread *thread; thread = thread_self(); - printk("trap: interrupted thread: %p (%s)\n", thread, thread->name); + printf("trap: interrupted thread: %p (%s)\n", thread, thread->name); } static void @@ -151,7 +151,7 @@ trap_double_fault(struct trap_frame *frame) frame->ss = cpu->tss.ss; #endif /* __LP64__ */ - printk("trap: double fault (cpu%u):\n", cpu_id()); + printf("trap: double fault (cpu%u):\n", cpu_id()); trap_show_thread(); trap_frame_show(frame); trap_stack_show(frame); @@ -169,7 +169,7 @@ static void trap_default(struct trap_frame *frame) { cpu_halt_broadcast(); - printk("trap: unhandled interrupt or exception (cpu%u):\n", cpu_id()); + printf("trap: unhandled interrupt or exception (cpu%u):\n", cpu_id()); trap_show_thread(); trap_frame_show(frame); trap_stack_show(frame); @@ -255,7 +255,7 @@ trap_main(struct trap_frame *frame) void trap_frame_show(struct trap_frame *frame) { - printk("trap: rax: %016lx rbx: %016lx rcx: %016lx\n" + printf("trap: rax: %016lx rbx: %016lx rcx: %016lx\n" "trap: rdx: %016lx rbp: %016lx rsi: %016lx\n" "trap: rdi: %016lx r8: %016lx r9: %016lx\n" "trap: r10: %016lx r11: %016lx r12: %016lx\n" @@ -277,7 +277,7 @@ trap_frame_show(struct trap_frame *frame) /* XXX Until the page fault handler is written */ if (frame->vector == 14) { - printk("trap: cr2: %016lx\n", (unsigned long)cpu_get_cr2()); + printf("trap: cr2: %016lx\n", (unsigned long)cpu_get_cr2()); } } @@ -296,7 +296,7 @@ trap_frame_show(struct trap_frame *frame) ss = 0; } - printk("trap: eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n" + printf("trap: eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n" "trap: ebp: %08lx esi: %08lx edi: %08lx\n" "trap: ds: %hu es: %hu fs: %hu gs: %hu\n" "trap: vector: %lu error: %08lx\n" @@ -315,7 +315,7 @@ trap_frame_show(struct trap_frame *frame) /* XXX Until the page fault handler is written */ if (frame->vector == 14) { - printk("trap: cr2: %08lx\n", (unsigned long)cpu_get_cr2()); + printf("trap: cr2: %08lx\n", (unsigned long)cpu_get_cr2()); } } diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index a9369fd..f38c0c4 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -70,7 +70,7 @@ #include #include -#include +#include #ifdef __LP64__ @@ -127,7 +127,7 @@ struct trap_frame { static inline void trap_test_double_fault(void) { - printk("trap: double fault test\n"); + printf("trap: double fault test\n"); asm volatile("movl $0xdead, %esp; push $0"); } diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 16635ad..c53f65f 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -119,7 +119,7 @@ module:kern/list:: Linked list. module:kern/macros:: Useful generic macros. -module:kern/printk:: +module:kern/printf:: Formatted output functions. module:kern/rbtree:: Red-black tree. diff --git a/doc/style.9.txt b/doc/style.9.txt index 94f2515..101f340 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -634,7 +634,7 @@ error = do_something(); if (error) { if (error != ERROR_AGAIN) { - printk("unexpected error\n"); + printf("unexpected error\n"); } return error; diff --git a/kern/kmem.c b/kern/kmem.c index d9ca7e4..60b8db9 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -57,7 +57,7 @@ #include #include #include -#include +#include #include #include #include @@ -441,7 +441,7 @@ kmem_cache_error(struct kmem_cache *cache, void *buf, int error, void *arg) { struct kmem_buftag *buftag; - printk("kmem: error: cache: %s, buffer: %p\n", cache->name, buf); + printf("kmem: error: cache: %s, buffer: %p\n", cache->name, buf); switch(error) { case KMEM_ERR_INVALID: @@ -1111,7 +1111,7 @@ kmem_cache_info(struct kmem_cache *cache) mutex_lock(&cache->lock); - printk("kmem: name: %s\n" + printf("kmem: name: %s\n" "kmem: flags: 0x%x%s\n" "kmem: obj_size: %zu\n" "kmem: align: %zu\n" @@ -1286,7 +1286,7 @@ kmem_info(void) struct kmem_cache *cache; size_t mem_usage, mem_reclaimable; - printk("kmem: cache obj slab bufs objs bufs " + printf("kmem: cache obj slab bufs objs bufs " " total reclaimable\n" "kmem: name size size /slab usage count " " memory memory\n"); @@ -1299,7 +1299,7 @@ kmem_info(void) mem_usage = (cache->nr_slabs * cache->slab_size) >> 10; mem_reclaimable = (cache->nr_free_slabs * cache->slab_size) >> 10; - printk("kmem: %-19s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", + printf("kmem: %-19s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", cache->name, cache->obj_size, cache->slab_size >> 10, cache->bufs_per_slab, cache->nr_objs, cache->nr_bufs, mem_usage, mem_reclaimable); diff --git a/kern/llsync.c b/kern/llsync.c index 023548a..0c81c0f 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -123,7 +123,7 @@ llsync_process_global_checkpoint(void) /* TODO Handle hysteresis */ if (!llsync_data.no_warning && (nr_works >= LLSYNC_NR_PENDING_WORKS_WARN)) { llsync_data.no_warning = 1; - printk("llsync: warning: large number of pending works\n"); + printf("llsync: warning: large number of pending works\n"); } if (llsync_data.nr_registered_cpus == 0) { diff --git a/kern/panic.c b/kern/panic.c index a9599f2..470c421 100644 --- a/kern/panic.c +++ b/kern/panic.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -42,10 +42,10 @@ panic(const char *format, ...) cpu_intr_disable(); cpu_halt_broadcast(); - printk("\npanic: "); + printf("\npanic: "); va_start(list, format); - vprintk(format, list); - printk("\n"); + vprintf(format, list); + printf("\n"); strace_dump(); cpu_halt(); diff --git a/kern/percpu.c b/kern/percpu.c index ad00772..6008b64 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -50,7 +50,7 @@ percpu_setup(void) unsigned int order; percpu_area_size = &_percpu_end - &_percpu; - printk("percpu: max_cpus: %u, section size: %zuk\n", X15_MAX_CPUS, + printf("percpu: max_cpus: %u, section size: %zuk\n", X15_MAX_CPUS, percpu_area_size >> 10); assert(vm_page_aligned(percpu_area_size)); @@ -77,7 +77,7 @@ percpu_add(unsigned int cpu) if (cpu >= ARRAY_SIZE(percpu_areas)) { if (!percpu_skip_warning) { - printk("percpu: ignoring processor beyond id %zu\n", + printf("percpu: ignoring processor beyond id %zu\n", ARRAY_SIZE(percpu_areas) - 1); percpu_skip_warning = 1; } @@ -86,7 +86,7 @@ percpu_add(unsigned int cpu) } if (percpu_areas[cpu] != NULL) { - printk("percpu: error: id %u ignored, already registered\n", cpu); + printf("percpu: error: id %u ignored, already registered\n", cpu); return ERROR_INVAL; } @@ -98,7 +98,7 @@ percpu_add(unsigned int cpu) page = vm_page_alloc(order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL); if (page == NULL) { - printk("percpu: error: unable to allocate percpu area\n"); + printf("percpu: error: unable to allocate percpu area\n"); return ERROR_NOMEM; } diff --git a/kern/printf.c b/kern/printf.c new file mode 100644 index 0000000..6cd6464 --- /dev/null +++ b/kern/printf.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010, 2012, 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 3 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 + +/* + * Size of the static buffer. + */ +#define PRINTK_BUFSIZE 1024 + +/* + * XXX Must be provided by a console driver. + */ +extern void console_write_byte(char c); + +static char printf_buffer[PRINTK_BUFSIZE]; +static struct spinlock printf_lock; + +int +printf(const char *format, ...) +{ + va_list ap; + int length; + + va_start(ap, format); + length = vprintf(format, ap); + va_end(ap); + + return length; +} + +int +vprintf(const char *format, va_list ap) +{ + unsigned long flags; + int length; + char *ptr; + + spinlock_lock_intr_save(&printf_lock, &flags); + + length = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); + + for (ptr = printf_buffer; *ptr != '\0'; ptr++) { + console_write_byte(*ptr); + } + + spinlock_unlock_intr_restore(&printf_lock, flags); + + return length; +} + +void +printf_setup(void) +{ + spinlock_init(&printf_lock); +} diff --git a/kern/printf.h b/kern/printf.h new file mode 100644 index 0000000..efaf2f9 --- /dev/null +++ b/kern/printf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010 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 3 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 . + * + * + * Formatted output functions. + * + * The printf() and vprintf() functions internally use a statically + * allocated buffer. They won't produce output larger than 1 KiB. They can + * be used safely in any context. + * + * See the sprintf module for information about the supported formats. + */ + +#ifndef _KERN_PRINTK_H +#define _KERN_PRINTK_H + +#include + +#include + +int printf(const char *format, ...) __format_printf(1, 2); +int vprintf(const char *format, va_list ap) __format_printf(1, 0); +void printf_setup(void); + +#endif /* _KERN_PRINTK_H */ diff --git a/kern/printk.c b/kern/printk.c deleted file mode 100644 index dd5cc87..0000000 --- a/kern/printk.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2010, 2012, 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 3 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 - -/* - * Size of the static buffer. - */ -#define PRINTK_BUFSIZE 1024 - -/* - * XXX Must be provided by a console driver. - */ -extern void console_write_byte(char c); - -static char printk_buffer[PRINTK_BUFSIZE]; -static struct spinlock printk_lock; - -int -printk(const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = vprintk(format, ap); - va_end(ap); - - return length; -} - -int -vprintk(const char *format, va_list ap) -{ - unsigned long flags; - int length; - char *ptr; - - spinlock_lock_intr_save(&printk_lock, &flags); - - length = vsnprintf(printk_buffer, sizeof(printk_buffer), format, ap); - - for (ptr = printk_buffer; *ptr != '\0'; ptr++) { - console_write_byte(*ptr); - } - - spinlock_unlock_intr_restore(&printk_lock, flags); - - return length; -} - -void -printk_setup(void) -{ - spinlock_init(&printk_lock); -} diff --git a/kern/printk.h b/kern/printk.h deleted file mode 100644 index 02173b3..0000000 --- a/kern/printk.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2010 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 3 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 . - * - * - * Formatted output functions. - * - * The printk() and vprintk() functions internally use a statically - * allocated buffer. They won't produce output larger than 1 KiB. They can - * be used safely in any context. - * - * See the sprintf module for information about the supported formats. - */ - -#ifndef _KERN_PRINTK_H -#define _KERN_PRINTK_H - -#include - -#include - -int printk(const char *format, ...) __format_printf(1, 2); -int vprintk(const char *format, va_list ap) __format_printf(1, 0); -void printk_setup(void); - -#endif /* _KERN_PRINTK_H */ diff --git a/kern/sref.c b/kern/sref.c index 23fb0ad..9c2ddea 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -503,7 +503,7 @@ sref_end_epoch(struct sref_queue *queue) if (!sref_data.no_warning && (sref_review_queue_size() >= SREF_NR_COUNTERS_WARN)) { sref_data.no_warning = 1; - printk("sref: warning: large number of counters in review queue\n"); + printf("sref: warning: large number of counters in review queue\n"); } if (sref_data.nr_registered_cpus == 1) { diff --git a/kern/syscnt.c b/kern/syscnt.c index 4336ef1..74559ea 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -61,7 +61,7 @@ syscnt_info(const char *prefix) prefix_length = (prefix == NULL) ? 0 : strlen(prefix); - printk("syscnt: name value\n"); + printf("syscnt: name value\n"); mutex_lock(&syscnt_lock); @@ -77,7 +77,7 @@ syscnt_info(const char *prefix) value = syscnt_read(syscnt); - printk("syscnt: %-30s %17llu\n", syscnt->name, + printf("syscnt: %-30s %17llu\n", syscnt->name, (unsigned long long)value); } diff --git a/kern/task.c b/kern/task.c index c7caeb4..d2aaa80 100644 --- a/kern/task.c +++ b/kern/task.c @@ -131,7 +131,7 @@ task_info(struct task *task) spinlock_lock(&task_list_lock); list_for_each_entry(&task_list, task, node) { - printk("task: %s\n", task->name); + printf("task: %s\n", task->name); } spinlock_unlock(&task_list_lock); @@ -141,7 +141,7 @@ task_info(struct task *task) spinlock_lock(&task->lock); - printk("task: name: %s, threads:\n", task->name); + printf("task: name: %s, threads:\n", task->name); /* * Don't grab any lock when accessing threads, so that the function @@ -150,7 +150,7 @@ task_info(struct task *task) * so holding the task lock is enough to guarantee existence. */ list_for_each_entry(&task->threads, thread, task_node) { - printk(TASK_INFO_ADDR_FMT " %c %8s:" TASK_INFO_ADDR_FMT + printf(TASK_INFO_ADDR_FMT " %c %8s:" TASK_INFO_ADDR_FMT " %.2s:%02hu %02u %s\n", (unsigned long)thread, thread_state_to_chr(thread), diff --git a/kern/work.c b/kern/work.c index f6309e7..a81e8de 100644 --- a/kern/work.c +++ b/kern/work.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -375,7 +375,7 @@ work_process(void *arg) if (error) { work_pool_free_id(pool, id); - printk("work: warning: unable to create worker thread\n"); + printf("work: warning: unable to create worker thread\n"); } } } @@ -491,7 +491,7 @@ work_setup(void) work_pool_init(&work_pool_highprio, WORK_INVALID_CPU, WORK_PF_GLOBAL | WORK_PF_HIGHPRIO); - printk("work: threads per pool (per-cpu/global): %u/%u, spare: %u\n", + printf("work: threads per pool (per-cpu/global): %u/%u, spare: %u\n", percpu_var(work_pool_cpu_main.max_threads, 0), work_pool_main.max_threads, WORK_THREADS_SPARE); } diff --git a/test/test_llsync_defer.c b/test/test_llsync_defer.c index 929ece6..9bce016 100644 --- a/test/test_llsync_defer.c +++ b/test/test_llsync_defer.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include @@ -91,7 +91,7 @@ test_alloc(void *arg) condition_signal(&test_condition); if ((i % TEST_LOOPS_PER_PRINT) == 0) { - printk("alloc "); + printf("alloc "); } i++; @@ -140,7 +140,7 @@ test_free(void *arg) condition_signal(&test_condition); if ((i % TEST_LOOPS_PER_PRINT) == 0) { - printk("free "); + printf("free "); } i++; @@ -173,7 +173,7 @@ test_read(void *arg) } if ((i % TEST_LOOPS_PER_PRINT) == 0) { - printk("read "); + printf("read "); } i++; diff --git a/test/test_mutex_pi.c b/test/test_mutex_pi.c index ca03b32..8f86475 100644 --- a/test/test_mutex_pi.c +++ b/test/test_mutex_pi.c @@ -84,7 +84,7 @@ #include #include #include -#include +#include #include #include #include @@ -181,7 +181,7 @@ test_for_priority_boosted(unsigned short *highest_priority) } if (real_priority > *highest_priority) { - printk("%c: real priority boosted to %s\n", + printf("%c: real priority boosted to %s\n", test_get_name(), test_thread_from_priority(real_priority)); *highest_priority = real_priority; } @@ -215,7 +215,7 @@ test_for_priority_deboosted(void) static void test_report_progress(unsigned int i) { - printk("%c:%u ", test_get_name(), i); + printf("%c:%u ", test_get_name(), i); } static void @@ -304,7 +304,7 @@ test_manage_b(void *arg) test_consume_cpu(); } - printk("b:%u ", i); + printf("b:%u ", i); syscnt_info("thread_boosts"); } } diff --git a/test/test_pmap_update_mp.c b/test/test_pmap_update_mp.c index 5f40c8c..0c5e2db 100644 --- a/test/test_pmap_update_mp.c +++ b/test/test_pmap_update_mp.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,12 +49,12 @@ test_run1(void *arg) (void)arg; - printk("allocating page\n"); + printf("allocating page\n"); ptr = vm_kmem_alloc(PAGE_SIZE); - printk("writing page\n"); + printf("writing page\n"); memset(ptr, 'a', PAGE_SIZE); - printk("passing page to second thread (%p)\n", ptr); + printf("passing page to second thread (%p)\n", ptr); mutex_lock(&test_lock); test_va = ptr; @@ -70,7 +70,7 @@ test_run2(void *arg) (void)arg; - printk("waiting for page\n"); + printf("waiting for page\n"); mutex_lock(&test_lock); @@ -82,7 +82,7 @@ test_run2(void *arg) mutex_unlock(&test_lock); - printk("page received (%p), checking page\n", ptr); + printf("page received (%p), checking page\n", ptr); for (i = 0; i < PAGE_SIZE; i++) { if (ptr[i] != 'a') { @@ -91,7 +91,7 @@ test_run2(void *arg) } vm_kmem_free(ptr, PAGE_SIZE); - printk("done\n"); + printf("done\n"); } void diff --git a/test/test_sref_dirty_zeroes.c b/test/test_sref_dirty_zeroes.c index 2b844d4..4099760 100644 --- a/test/test_sref_dirty_zeroes.c +++ b/test/test_sref_dirty_zeroes.c @@ -67,7 +67,7 @@ test_inc(void *arg) mutex_unlock(&test_lock); } - printk("counter global value: %lu\n", test_counter.value); + printf("counter global value: %lu\n", test_counter.value); syscnt_info("sref_epoch"); syscnt_info("sref_dirty_zero"); syscnt_info("sref_true_zero"); diff --git a/test/test_sref_noref.c b/test/test_sref_noref.c index d7dbc3d..b98edd3 100644 --- a/test/test_sref_noref.c +++ b/test/test_sref_noref.c @@ -76,7 +76,7 @@ test_ref(void *arg) mutex_lock(&test_lock); - printk("waiting for page\n"); + printf("waiting for page\n"); while (test_obj == NULL) { condition_wait(&test_condition, &test_lock); @@ -86,13 +86,13 @@ test_ref(void *arg) mutex_unlock(&test_lock); - printk("page received, manipulate reference counter\n"); + printf("page received, manipulate reference counter\n"); while (!test_stop) { test_manipulate_counter(obj); } - printk("thread exiting\n"); + printf("thread exiting\n"); } static void @@ -102,7 +102,7 @@ test_obj_noref(struct sref_counter *counter) obj = structof(counter, struct test_obj, ref_counter); vm_kmem_free(obj, sizeof(*obj)); - printk("0 references, page released\n"); + printf("0 references, page released\n"); syscnt_info("sref_epoch"); syscnt_info("sref_dirty_zero"); syscnt_info("sref_true_zero"); @@ -135,7 +135,7 @@ test_run(void *arg) error_check(error, "thread_create"); } - printk("allocating page\n"); + printf("allocating page\n"); obj = vm_kmem_alloc(sizeof(*obj)); if (obj == NULL) { @@ -144,7 +144,7 @@ test_run(void *arg) sref_counter_init(&obj->ref_counter, NULL, test_obj_noref); - printk("page allocated, 1 reference, publishing\n"); + printf("page allocated, 1 reference, publishing\n"); mutex_lock(&test_lock); test_obj = obj; @@ -155,14 +155,14 @@ test_run(void *arg) test_manipulate_counter(obj); } - printk("stopping test, wait for threads\n"); + printf("stopping test, wait for threads\n"); test_stop = 1; for (i = 0; i < nr_threads; i++) { thread_join(threads[i]); } - printk("releasing initial reference\n"); + printf("releasing initial reference\n"); sref_counter_dec(&obj->ref_counter); kmem_free(threads, sizeof(*threads) * nr_threads); diff --git a/test/test_sref_weakref.c b/test/test_sref_weakref.c index e28851f..65918a1 100644 --- a/test/test_sref_weakref.c +++ b/test/test_sref_weakref.c @@ -71,7 +71,7 @@ test_run(void *arg) for (j = 0; j < 0x20000000; j++); - printk("run: iterations: %lu\n", i); + printf("run: iterations: %lu\n", i); syscnt_info("sref_epoch"); syscnt_info("sref_dirty_zero"); syscnt_info("sref_revive"); @@ -95,7 +95,7 @@ test_ref(void *arg) } if ((i % 100000000) == 0) { - printk("ref: iterations: %lu\n", i); + printf("ref: iterations: %lu\n", i); } } } diff --git a/test/test_vm_page_fill.c b/test/test_vm_page_fill.c index c334407..7171199 100644 --- a/test/test_vm_page_fill.c +++ b/test/test_vm_page_fill.c @@ -115,7 +115,7 @@ test_run(void *arg) (void)arg; for (i = 0; /* no condition */; i++) { - printk("test: pass:%u pattern:%hhx\n", i, test_pattern); + printf("test: pass:%u pattern:%hhx\n", i, test_pattern); test_write_pages(); test_reset_pages(); test_pattern++; diff --git a/test/test_xcall.c b/test/test_xcall.c index ab4fa11..3552944 100644 --- a/test/test_xcall.c +++ b/test/test_xcall.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,7 +37,7 @@ test_fn(void *arg) { (void)arg; - printk("function called, running on cpu%u\n", cpu_id()); + printf("function called, running on cpu%u\n", cpu_id()); test_done = 1; } @@ -46,7 +46,7 @@ test_once(unsigned int cpu) { test_done = 0; - printk("cross-call on cpu%u:\n", cpu); + printf("cross-call on cpu%u:\n", cpu); xcall_call(test_fn, NULL, cpu); if (!test_done) { @@ -61,7 +61,7 @@ test_run(void *arg) test_once(0); test_once(1); - printk("done\n"); + printf("done\n"); } void diff --git a/vm/vm_map.c b/vm/vm_map.c index 7e91f9e..217aae7 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include @@ -751,7 +751,7 @@ vm_map_info(struct vm_map *map) mutex_lock(&map->lock); - printk("vm_map: %s: %016lx-%016lx\n" + printf("vm_map: %s: %016lx-%016lx\n" "vm_map: start end " "size offset flags type\n", name, (unsigned long)map->start, (unsigned long)map->end); @@ -763,13 +763,13 @@ vm_map_info(struct vm_map *map) type = "object"; } - printk("vm_map: %016lx %016lx %8luk %08llx %08x %s\n", + printf("vm_map: %016lx %016lx %8luk %08llx %08x %s\n", (unsigned long)entry->start, (unsigned long)entry->end, (unsigned long)(entry->end - entry->start) >> 10, (unsigned long long)entry->offset, entry->flags, type); } - printk("vm_map: total: %zuk\n", map->size >> 10); + printf("vm_map: total: %zuk\n", map->size >> 10); mutex_unlock(&map->lock); } diff --git a/vm/vm_page.c b/vm/vm_page.c index cbbfd13..138f028 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include #include @@ -512,7 +512,7 @@ vm_page_load(unsigned int zone_index, phys_addr_t start, phys_addr_t end) zone->heap_present = false; #if DEBUG - printk("vm_page: load: %s: %llx:%llx\n", + printf("vm_page: load: %s: %llx:%llx\n", vm_page_zone_name(zone_index), (unsigned long long)start, (unsigned long long)end); #endif @@ -539,7 +539,7 @@ vm_page_load_heap(unsigned int zone_index, phys_addr_t start, phys_addr_t end) zone->heap_present = true; #if DEBUG - printk("vm_page: heap: %s: %llx:%llx\n", + printf("vm_page: heap: %s: %llx:%llx\n", vm_page_zone_name(zone_index), (unsigned long long)start, (unsigned long long)end); #endif @@ -664,7 +664,7 @@ vm_page_setup(void) } table_size = vm_page_round(nr_pages * sizeof(struct vm_page)); - printk("vm_page: page table size: %zu entries (%zuk)\n", nr_pages, + printf("vm_page: page table size: %zu entries (%zuk)\n", nr_pages, table_size >> 10); table = vm_page_bootalloc(table_size); va = (uintptr_t)table; @@ -782,7 +782,7 @@ vm_page_info(void) for (i = 0; i < vm_page_zones_size; i++) { zone = &vm_page_zones[i]; pages = (unsigned long)(zone->pages_end - zone->pages); - printk("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)\n", + printf("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)\n", vm_page_zone_name(i), pages, pages >> (20 - PAGE_SHIFT), zone->nr_free_pages, zone->nr_free_pages >> (20 - PAGE_SHIFT)); } -- cgit v1.2.3 From 5a048710ee9dd4112ec467da29fef27ef18b0876 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 21:47:28 +0200 Subject: New stdio.h standard header Make kernel code obtain definitions for the printf family of functions through the inclusion of the standard stdio.h header. --- Makefrag.am | 1 + arch/x86/machine/acpimp.c | 2 +- arch/x86/machine/biosmem.c | 2 +- arch/x86/machine/boot.c | 2 +- arch/x86/machine/cpu.c | 2 +- arch/x86/machine/lapic.c | 2 +- arch/x86/machine/pmap.c | 2 +- arch/x86/machine/strace.c | 2 +- arch/x86/machine/trap.c | 3 ++- arch/x86/machine/trap.h | 2 +- include/stdio.h | 24 ++++++++++++++++++++++++ kern/kmem.c | 3 +-- kern/llsync.c | 3 +-- kern/panic.c | 2 +- kern/percpu.c | 2 +- kern/printf.c | 4 ++-- kern/sprintf.c | 2 +- kern/sref.c | 2 +- kern/syscnt.c | 2 +- kern/task.c | 1 + kern/thread.c | 2 +- kern/work.c | 3 +-- test/test_llsync_defer.c | 2 +- test/test_mutex_pi.c | 2 +- test/test_pmap_update_mp.c | 2 +- test/test_sref_dirty_zeroes.c | 2 +- test/test_sref_noref.c | 2 +- test/test_sref_weakref.c | 1 + test/test_vm_page_fill.c | 1 + test/test_xcall.c | 2 +- vm/vm_map.c | 2 +- vm/vm_page.c | 3 +-- 32 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 include/stdio.h diff --git a/Makefrag.am b/Makefrag.am index b7013c2..8a17c18 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -4,6 +4,7 @@ include doc/Makefrag.am EXTRA_DIST += tools/qemu.sh x15_SOURCES += \ + include/stdio.h \ include/string.h x15_SOURCES += \ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 527b305..8d2433a 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 060be44..35e015b 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index e787901..011a319 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -54,7 +55,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 1fd3fa8..ff7b722 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 90886c7..89affc5 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -17,12 +17,12 @@ #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 63a2909..1de9b09 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index 89f8569..8aea4b0 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -16,12 +16,12 @@ */ #include +#include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 2d25ce9..9a2fe15 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -19,11 +19,12 @@ * additional configuration and resources to be properly handled. */ +#include + #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index f38c0c4..a652382 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -68,9 +68,9 @@ #ifndef __ASSEMBLER__ #include +#include #include -#include #ifdef __LP64__ diff --git a/include/stdio.h b/include/stdio.h new file mode 100644 index 0000000..a9aed9e --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 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 3 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 _STDIO_H +#define _STDIO_H + +#include +#include + +#endif /* _STDIO_H */ diff --git a/kern/kmem.c b/kern/kmem.c index 60b8db9..5cce558 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -57,8 +58,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/kern/llsync.c b/kern/llsync.c index 0c81c0f..1942576 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -46,9 +47,7 @@ #include #include #include -#include #include -#include #include #include #include diff --git a/kern/panic.c b/kern/panic.c index 470c421..34d8c13 100644 --- a/kern/panic.c +++ b/kern/panic.c @@ -16,10 +16,10 @@ */ #include +#include #include #include -#include #include #include diff --git a/kern/percpu.c b/kern/percpu.c index 6008b64..5e4ff82 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/printf.c b/kern/printf.c index 6cd6464..7e48b5d 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -#include +#include + #include -#include #include /* diff --git a/kern/sprintf.c b/kern/sprintf.c index 3d2bf2d..a606d86 100644 --- a/kern/sprintf.c +++ b/kern/sprintf.c @@ -18,9 +18,9 @@ #include #include #include +#include #include -#include #include /* diff --git a/kern/sref.c b/kern/sref.c index 9c2ddea..9ce7a83 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -55,7 +56,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/syscnt.c b/kern/syscnt.c index 74559ea..5b479f3 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -15,13 +15,13 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include #include diff --git a/kern/task.c b/kern/task.c index d2aaa80..7f1c53d 100644 --- a/kern/task.c +++ b/kern/task.c @@ -16,6 +16,7 @@ */ #include +#include #include #include diff --git a/kern/thread.c b/kern/thread.c index 8a87aca..947b8cf 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -83,6 +83,7 @@ #include #include +#include #include #include @@ -101,7 +102,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/work.c b/kern/work.c index a81e8de..714af35 100644 --- a/kern/work.c +++ b/kern/work.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -27,9 +28,7 @@ #include #include #include -#include #include -#include #include #include #include diff --git a/test/test_llsync_defer.c b/test/test_llsync_defer.c index 9bce016..2674799 100644 --- a/test/test_llsync_defer.c +++ b/test/test_llsync_defer.c @@ -29,6 +29,7 @@ */ #include +#include #include #include @@ -39,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test_mutex_pi.c b/test/test_mutex_pi.c index 8f86475..cfadb59 100644 --- a/test/test_mutex_pi.c +++ b/test/test_mutex_pi.c @@ -78,13 +78,13 @@ */ #include +#include #include #include #include #include #include -#include #include #include #include diff --git a/test/test_pmap_update_mp.c b/test/test_pmap_update_mp.c index 0c5e2db..5588ce8 100644 --- a/test/test_pmap_update_mp.c +++ b/test/test_pmap_update_mp.c @@ -25,6 +25,7 @@ */ #include +#include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test_sref_dirty_zeroes.c b/test/test_sref_dirty_zeroes.c index 4099760..be4c491 100644 --- a/test/test_sref_dirty_zeroes.c +++ b/test/test_sref_dirty_zeroes.c @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -34,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test_sref_noref.c b/test/test_sref_noref.c index b98edd3..f556d32 100644 --- a/test/test_sref_noref.c +++ b/test/test_sref_noref.c @@ -33,6 +33,7 @@ */ #include +#include #include #include @@ -40,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test_sref_weakref.c b/test/test_sref_weakref.c index 65918a1..85d4cbe 100644 --- a/test/test_sref_weakref.c +++ b/test/test_sref_weakref.c @@ -33,6 +33,7 @@ */ #include +#include #include #include diff --git a/test/test_vm_page_fill.c b/test/test_vm_page_fill.c index 7171199..fe35103 100644 --- a/test/test_vm_page_fill.c +++ b/test/test_vm_page_fill.c @@ -24,6 +24,7 @@ #include #include +#include #include #include diff --git a/test/test_xcall.c b/test/test_xcall.c index 3552944..4e02d6f 100644 --- a/test/test_xcall.c +++ b/test/test_xcall.c @@ -21,11 +21,11 @@ */ #include +#include #include #include #include -#include #include #include #include diff --git a/vm/vm_map.c b/vm/vm_map.c index 217aae7..657f4b5 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/vm/vm_page.c b/vm/vm_page.c index 138f028..284964a 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -41,8 +42,6 @@ #include #include #include -#include -#include #include #include #include -- cgit v1.2.3 From 3a478529acaadb59315f9022360f2b3bb00fec2f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:02:55 +0200 Subject: kern/thread: include stdint.h --- kern/thread.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kern/thread.c b/kern/thread.c index 947b8cf..42150e6 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -83,6 +83,7 @@ #include #include +#include #include #include -- cgit v1.2.3 From 5a207346dd1b7ef10c4cf7bc5d801e7369bb7004 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:07:44 +0200 Subject: Replace unneeded tab characters in Makefiles This is done according to the style policy which states that "Tabulation characters are strictly forbidden in source files, and should only be used in Makefiles where absolutely required". --- Makefile.am | 20 +++--- Makefrag.am | 194 +++++++++++++++++++++++++-------------------------- arch/x86/Makefrag.am | 76 ++++++++++---------- doc/Makefrag.am | 4 +- tools/qemu.sh | 2 +- 5 files changed, 148 insertions(+), 148 deletions(-) diff --git a/Makefile.am b/Makefile.am index 0d2dec9..f7f0b09 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,24 +13,24 @@ AM_CPPFLAGS += -nostdinc AM_CPPFLAGS += -isystem $(shell $(CC) -print-file-name=include) AM_CPPFLAGS += \ - -I$(top_srcdir) \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/arch/$(arch) + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/arch/$(arch) AM_CFLAGS += -ffreestanding AM_CFLAGS += -std=gnu11 AM_CFLAGS += \ - -Wall \ - -Wextra \ - -Wshadow \ - -Wmissing-prototypes \ - -Wstrict-prototypes + -Wall \ + -Wextra \ + -Wshadow \ + -Wmissing-prototypes \ + -Wstrict-prototypes AM_CFLAGS += \ - -fsigned-char \ - -fno-common + -fsigned-char \ + -fno-common # TODO Add stack protector support AM_CFLAGS += -fno-stack-protector diff --git a/Makefrag.am b/Makefrag.am index 8a17c18..90c146b 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -4,109 +4,109 @@ include doc/Makefrag.am EXTRA_DIST += tools/qemu.sh x15_SOURCES += \ - include/stdio.h \ - include/string.h + include/stdio.h \ + include/string.h x15_SOURCES += \ - kern/assert.h \ - kern/atomic.h \ - kern/bitmap.c \ - kern/bitmap.h \ - kern/bitmap_i.h \ - kern/condition.c \ - kern/condition.h \ - kern/condition_types.h \ - kern/config.h \ - kern/cpumap.c \ - kern/cpumap.h \ - kern/error.c \ - kern/error.h \ - kern/init.h \ - kern/kernel.c \ - kern/kernel.h \ - kern/kmem.c \ - kern/kmem.h \ - kern/kmem_i.h \ - kern/limits.h \ - kern/list.h \ - kern/list_types.h \ - kern/llsync.c \ - kern/llsync.h \ - kern/llsync_i.h \ - kern/log2.h \ - kern/macros.h \ - kern/mutex.c \ - kern/mutex.h \ - kern/mutex_i.h \ - kern/mutex_types.h \ - kern/panic.c \ - kern/panic.h \ - kern/param.h \ - kern/percpu.c \ - kern/percpu.h \ - kern/plist.c \ - kern/plist.h \ - kern/plist_types.h \ - kern/printf.c \ - kern/printf.h \ - kern/rbtree.c \ - kern/rbtree.h \ - kern/rbtree_i.h \ - kern/rdxtree.c \ - kern/rdxtree.h \ - kern/rdxtree_i.h \ - kern/rtmutex.c \ - kern/rtmutex.h \ - kern/rtmutex_i.h \ - kern/rtmutex_types.h \ - kern/semaphore.c \ - kern/semaphore.h \ - kern/semaphore_i.h \ - kern/sleepq.c \ - kern/sleepq.h \ - kern/spinlock.c \ - kern/spinlock.h \ - kern/spinlock_i.h \ - kern/spinlock_types.h \ - kern/sprintf.c \ - kern/sprintf.h \ - kern/sref.c \ - kern/sref.h \ - kern/sref_i.h \ - kern/string.c \ - kern/string.h \ - kern/syscnt.c \ - kern/syscnt.h \ - kern/syscnt_types.h \ - kern/task.c \ - kern/task.h \ - kern/thread.c \ - kern/thread.h \ - kern/thread_i.h \ - kern/turnstile.c \ - kern/turnstile.h \ - kern/turnstile_types.h \ - kern/types.h \ - kern/work.c \ - kern/work.h \ - kern/xcall.c \ - kern/xcall.h + kern/assert.h \ + kern/atomic.h \ + kern/bitmap.c \ + kern/bitmap.h \ + kern/bitmap_i.h \ + kern/condition.c \ + kern/condition.h \ + kern/condition_types.h \ + kern/config.h \ + kern/cpumap.c \ + kern/cpumap.h \ + kern/error.c \ + kern/error.h \ + kern/init.h \ + kern/kernel.c \ + kern/kernel.h \ + kern/kmem.c \ + kern/kmem.h \ + kern/kmem_i.h \ + kern/limits.h \ + kern/list.h \ + kern/list_types.h \ + kern/llsync.c \ + kern/llsync.h \ + kern/llsync_i.h \ + kern/log2.h \ + kern/macros.h \ + kern/mutex.c \ + kern/mutex.h \ + kern/mutex_i.h \ + kern/mutex_types.h \ + kern/panic.c \ + kern/panic.h \ + kern/param.h \ + kern/percpu.c \ + kern/percpu.h \ + kern/plist.c \ + kern/plist.h \ + kern/plist_types.h \ + kern/printf.c \ + kern/printf.h \ + kern/rbtree.c \ + kern/rbtree.h \ + kern/rbtree_i.h \ + kern/rdxtree.c \ + kern/rdxtree.h \ + kern/rdxtree_i.h \ + kern/rtmutex.c \ + kern/rtmutex.h \ + kern/rtmutex_i.h \ + kern/rtmutex_types.h \ + kern/semaphore.c \ + kern/semaphore.h \ + kern/semaphore_i.h \ + kern/sleepq.c \ + kern/sleepq.h \ + kern/spinlock.c \ + kern/spinlock.h \ + kern/spinlock_i.h \ + kern/spinlock_types.h \ + kern/sprintf.c \ + kern/sprintf.h \ + kern/sref.c \ + kern/sref.h \ + kern/sref_i.h \ + kern/string.c \ + kern/string.h \ + kern/syscnt.c \ + kern/syscnt.h \ + kern/syscnt_types.h \ + kern/task.c \ + kern/task.h \ + kern/thread.c \ + kern/thread.h \ + kern/thread_i.h \ + kern/turnstile.c \ + kern/turnstile.h \ + kern/turnstile_types.h \ + kern/types.h \ + kern/work.c \ + kern/work.h \ + kern/xcall.c \ + kern/xcall.h x15_SOURCES += \ - vm/vm_adv.h \ - vm/vm_inherit.h \ - vm/vm_kmem.c \ - vm/vm_kmem.h \ - vm/vm_map.c \ - vm/vm_map.h \ - vm/vm_page.c \ - vm/vm_page.h \ - vm/vm_prot.h \ - vm/vm_setup.c \ - vm/vm_setup.h + vm/vm_adv.h \ + vm/vm_inherit.h \ + vm/vm_kmem.c \ + vm/vm_kmem.h \ + vm/vm_map.c \ + vm/vm_map.h \ + vm/vm_page.c \ + vm/vm_page.h \ + vm/vm_prot.h \ + vm/vm_setup.c \ + vm/vm_setup.h x15_SOURCES += \ - test/test.h + test/test.h if TEST_LLSYNC_DEFER x15_SOURCES += test/test_llsync_defer.c diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 224ca59..5bedee0 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -24,43 +24,43 @@ x15_LDFLAGS += -m64 endif AMD64 x15_SOURCES += \ - arch/x86/machine/acpimp.c \ - arch/x86/machine/acpimp.h \ - arch/x86/machine/asm.h \ - arch/x86/machine/atomic.h \ - arch/x86/machine/biosmem.c \ - arch/x86/machine/biosmem.h \ - arch/x86/machine/boot_asm.S \ - arch/x86/machine/boot.c \ - arch/x86/machine/boot.h \ - arch/x86/machine/config.h \ - arch/x86/machine/cga.c \ - arch/x86/machine/cga.h \ - arch/x86/machine/cpu_asm.S \ - arch/x86/machine/cpu.c \ - arch/x86/machine/cpu.h \ - arch/x86/machine/elf.h \ - arch/x86/machine/io.h \ - arch/x86/machine/lapic.c \ - arch/x86/machine/lapic.h \ - arch/x86/machine/mb.h \ - arch/x86/machine/multiboot.h \ - arch/x86/machine/param.h \ - arch/x86/machine/pic.c \ - arch/x86/machine/pic.h \ - arch/x86/machine/pit.c \ - arch/x86/machine/pit.h \ - arch/x86/machine/pmap.c \ - arch/x86/machine/pmap.h \ - arch/x86/machine/strace.c \ - arch/x86/machine/strace.h \ - arch/x86/machine/string.c \ - arch/x86/machine/tcb_asm.S \ - arch/x86/machine/tcb.c \ - arch/x86/machine/tcb.h \ - arch/x86/machine/trap_asm.S \ - arch/x86/machine/trap.c \ - arch/x86/machine/trap.h \ - arch/x86/machine/types.h + arch/x86/machine/acpimp.c \ + arch/x86/machine/acpimp.h \ + arch/x86/machine/asm.h \ + arch/x86/machine/atomic.h \ + arch/x86/machine/biosmem.c \ + arch/x86/machine/biosmem.h \ + arch/x86/machine/boot_asm.S \ + arch/x86/machine/boot.c \ + arch/x86/machine/boot.h \ + arch/x86/machine/config.h \ + arch/x86/machine/cga.c \ + arch/x86/machine/cga.h \ + arch/x86/machine/cpu_asm.S \ + arch/x86/machine/cpu.c \ + arch/x86/machine/cpu.h \ + arch/x86/machine/elf.h \ + arch/x86/machine/io.h \ + arch/x86/machine/lapic.c \ + arch/x86/machine/lapic.h \ + arch/x86/machine/mb.h \ + arch/x86/machine/multiboot.h \ + arch/x86/machine/param.h \ + arch/x86/machine/pic.c \ + arch/x86/machine/pic.h \ + arch/x86/machine/pit.c \ + arch/x86/machine/pit.h \ + arch/x86/machine/pmap.c \ + arch/x86/machine/pmap.h \ + arch/x86/machine/strace.c \ + arch/x86/machine/strace.h \ + arch/x86/machine/string.c \ + arch/x86/machine/tcb_asm.S \ + arch/x86/machine/tcb.c \ + arch/x86/machine/tcb.h \ + arch/x86/machine/trap_asm.S \ + arch/x86/machine/trap.c \ + arch/x86/machine/trap.h \ + arch/x86/machine/types.h endif X86 diff --git a/doc/Makefrag.am b/doc/Makefrag.am index 8d8b3e0..0869fdf 100644 --- a/doc/Makefrag.am +++ b/doc/Makefrag.am @@ -1,8 +1,8 @@ EXTRA_DIST += doc/asciidoc.conf x15_DOCS = \ - doc/intro.9.txt \ - doc/style.9.txt + doc/intro.9.txt \ + doc/style.9.txt x15_SOURCES += $(x15_DOCS) diff --git a/tools/qemu.sh b/tools/qemu.sh index 8de129a..1e1990b 100755 --- a/tools/qemu.sh +++ b/tools/qemu.sh @@ -30,7 +30,7 @@ cat > $CDROOT/boot/grub/grub.cfg << EOF set timeout=1 menuentry "X15" --class os { - multiboot (hd96)/boot/x15 root=device:hd1s8 + multiboot (hd96)/boot/x15 root=device:hd1s8 } EOF grub-mkrescue -o $TMPDIR/grub.iso $CDROOT -- cgit v1.2.3 From 0cca3fe1f195b38cd7f51d25a7dd35e0a923bb63 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:33:41 +0200 Subject: Rename custom autoconf macros from ax_check_xxx to ax_prog_xxx With those names, the macros better conform with those found in the Autoconf Archive. --- configure.ac | 8 ++++---- tools/m4/ax_check_a2x.m4 | 5 ----- tools/m4/ax_check_asciidoc.m4 | 5 ----- tools/m4/ax_check_highlight.m4 | 5 ----- tools/m4/ax_prog_a2x.m4 | 5 +++++ tools/m4/ax_prog_asciidoc.m4 | 5 +++++ tools/m4/ax_prog_highlight.m4 | 5 +++++ 7 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 tools/m4/ax_check_a2x.m4 delete mode 100644 tools/m4/ax_check_asciidoc.m4 delete mode 100644 tools/m4/ax_check_highlight.m4 create mode 100644 tools/m4/ax_prog_a2x.m4 create mode 100644 tools/m4/ax_prog_asciidoc.m4 create mode 100644 tools/m4/ax_prog_highlight.m4 diff --git a/configure.ac b/configure.ac index 54b0e25..be21f3d 100644 --- a/configure.ac +++ b/configure.ac @@ -20,10 +20,10 @@ AC_PROG_CC AM_PROG_AS AM_PROG_CC_C_O -AX_CHECK_ASCIIDOC([AC_MSG_NOTICE([asciidoc not found, not building html documentation])]) -AX_CHECK_HIGHLIGHT([AS_IF([test x"$ASCIIDOC" != x], - [AC_MSG_NOTICE([highlight not found, not building html documentation])])]) -AX_CHECK_A2X([AC_MSG_NOTICE([a2x not found, not building man pages])]) +AX_PROG_ASCIIDOC([AC_MSG_NOTICE([asciidoc not found, not building html documentation])]) +AX_PROG_HIGHLIGHT([AS_IF([test x"$ASCIIDOC" != x], + [AC_MSG_NOTICE([highlight not found, not building html documentation])])]) +AX_PROG_A2X([AC_MSG_NOTICE([a2x not found, not building man pages])]) # Disable PIE if enabled by default AX_APPEND_COMPILE_FLAGS([-no-pie -fno-pie]) diff --git a/tools/m4/ax_check_a2x.m4 b/tools/m4/ax_check_a2x.m4 deleted file mode 100644 index 438e247..0000000 --- a/tools/m4/ax_check_a2x.m4 +++ /dev/null @@ -1,5 +0,0 @@ -AC_DEFUN([AX_CHECK_A2X], - [AC_ARG_VAR([A2X], [path to a2x command]) - AC_CHECK_PROG([A2X], [a2x], [a2x]) - AS_IF([test x"$A2X" = x], [$1]) - AM_CONDITIONAL([HAVE_A2X], [test x"$A2X" != x])]) diff --git a/tools/m4/ax_check_asciidoc.m4 b/tools/m4/ax_check_asciidoc.m4 deleted file mode 100644 index 5f6132a..0000000 --- a/tools/m4/ax_check_asciidoc.m4 +++ /dev/null @@ -1,5 +0,0 @@ -AC_DEFUN([AX_CHECK_ASCIIDOC], - [AC_ARG_VAR([ASCIIDOC], [path to asciidoc command]) - AC_CHECK_PROG([ASCIIDOC], [asciidoc], [asciidoc]) - AS_IF([test x"$ASCIIDOC" = x], [$1]) - AM_CONDITIONAL([HAVE_ASCIIDOC], [test x"$ASCIIDOC" != x])]) diff --git a/tools/m4/ax_check_highlight.m4 b/tools/m4/ax_check_highlight.m4 deleted file mode 100644 index c72f128..0000000 --- a/tools/m4/ax_check_highlight.m4 +++ /dev/null @@ -1,5 +0,0 @@ -AC_DEFUN([AX_CHECK_HIGHLIGHT], - [AC_ARG_VAR([HIGHLIGHT], [path to highlight command]) - AC_CHECK_PROG([HIGHLIGHT], [highlight], [highlight]) - AS_IF([test x"$HIGHLIGHT" = x], [$1]) - AM_CONDITIONAL([HAVE_HIGHLIGHT], [test x"$HIGHLIGHT" != x])]) diff --git a/tools/m4/ax_prog_a2x.m4 b/tools/m4/ax_prog_a2x.m4 new file mode 100644 index 0000000..e121481 --- /dev/null +++ b/tools/m4/ax_prog_a2x.m4 @@ -0,0 +1,5 @@ +AC_DEFUN([AX_PROG_A2X], + [AC_ARG_VAR([A2X], [path to a2x command]) + AC_CHECK_PROG([A2X], [a2x], [a2x]) + AS_IF([test x"$A2X" = x], [$1]) + AM_CONDITIONAL([HAVE_A2X], [test x"$A2X" != x])]) diff --git a/tools/m4/ax_prog_asciidoc.m4 b/tools/m4/ax_prog_asciidoc.m4 new file mode 100644 index 0000000..fb4f00f --- /dev/null +++ b/tools/m4/ax_prog_asciidoc.m4 @@ -0,0 +1,5 @@ +AC_DEFUN([AX_PROG_ASCIIDOC], + [AC_ARG_VAR([ASCIIDOC], [path to asciidoc command]) + AC_CHECK_PROG([ASCIIDOC], [asciidoc], [asciidoc]) + AS_IF([test x"$ASCIIDOC" = x], [$1]) + AM_CONDITIONAL([HAVE_ASCIIDOC], [test x"$ASCIIDOC" != x])]) diff --git a/tools/m4/ax_prog_highlight.m4 b/tools/m4/ax_prog_highlight.m4 new file mode 100644 index 0000000..c98316c --- /dev/null +++ b/tools/m4/ax_prog_highlight.m4 @@ -0,0 +1,5 @@ +AC_DEFUN([AX_PROG_HIGHLIGHT], + [AC_ARG_VAR([HIGHLIGHT], [path to highlight command]) + AC_CHECK_PROG([HIGHLIGHT], [highlight], [highlight]) + AS_IF([test x"$HIGHLIGHT" = x], [$1]) + AM_CONDITIONAL([HAVE_HIGHLIGHT], [test x"$HIGHLIGHT" != x])]) -- cgit v1.2.3 From a9fedfa33cdcb3a3ea7feae06220609f8f3db0e6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:48:27 +0200 Subject: Add license notice for the custom autoconf macros --- tools/m4/ax_prog_a2x.m4 | 41 +++++++++++++++++++++++++++++++++++++++++ tools/m4/ax_prog_asciidoc.m4 | 41 +++++++++++++++++++++++++++++++++++++++++ tools/m4/ax_prog_highlight.m4 | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) diff --git a/tools/m4/ax_prog_a2x.m4 b/tools/m4/ax_prog_a2x.m4 index e121481..5695003 100644 --- a/tools/m4/ax_prog_a2x.m4 +++ b/tools/m4/ax_prog_a2x.m4 @@ -1,3 +1,44 @@ +# SYNOPSIS +# +# AX_PROG_A2X([ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Check whether the a2x command is available on system, and provide +# the HAVE_A2X Automake conditional. +# +# ACTION-IF-NOT-FOUND is shell commands to execute on failure. +# +# LICENSE +# +# Copyright (c) 2017 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 3 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + AC_DEFUN([AX_PROG_A2X], [AC_ARG_VAR([A2X], [path to a2x command]) AC_CHECK_PROG([A2X], [a2x], [a2x]) diff --git a/tools/m4/ax_prog_asciidoc.m4 b/tools/m4/ax_prog_asciidoc.m4 index fb4f00f..b251cb6 100644 --- a/tools/m4/ax_prog_asciidoc.m4 +++ b/tools/m4/ax_prog_asciidoc.m4 @@ -1,3 +1,44 @@ +# SYNOPSIS +# +# AX_PROG_ASCIIDOC([ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Check whether the asciidoc command is available on system, and provide +# the HAVE_ASCIIDOC Automake conditional. +# +# ACTION-IF-NOT-FOUND is shell commands to execute on failure. +# +# LICENSE +# +# Copyright (c) 2017 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 3 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + AC_DEFUN([AX_PROG_ASCIIDOC], [AC_ARG_VAR([ASCIIDOC], [path to asciidoc command]) AC_CHECK_PROG([ASCIIDOC], [asciidoc], [asciidoc]) diff --git a/tools/m4/ax_prog_highlight.m4 b/tools/m4/ax_prog_highlight.m4 index c98316c..647c628 100644 --- a/tools/m4/ax_prog_highlight.m4 +++ b/tools/m4/ax_prog_highlight.m4 @@ -1,3 +1,44 @@ +# SYNOPSIS +# +# AX_PROG_HIGHLIGHT([ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# Check whether the highlight command is available on system, and provide +# the HAVE_HIGHLIGHT Automake conditional. +# +# ACTION-IF-NOT-FOUND is shell commands to execute on failure. +# +# LICENSE +# +# Copyright (c) 2017 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 3 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 . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + AC_DEFUN([AX_PROG_HIGHLIGHT], [AC_ARG_VAR([HIGHLIGHT], [path to highlight command]) AC_CHECK_PROG([HIGHLIGHT], [highlight], [highlight]) -- cgit v1.2.3 From fb13a50dca9a526c6a1666fefe123b58524040ff Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:51:58 +0200 Subject: Revert "Remove unneeded quotes when using #error" This reverts commit b1aa94d0c7dba4138f651e6b081c1a0055ea8d54. New occurrences of #error are also affected by this commit. --- arch/x86/machine/asm.h | 2 +- kern/rdxtree.c | 2 +- kern/sleepq.c | 2 +- kern/spinlock.c | 8 ++++---- kern/turnstile.c | 2 +- vm/vm_kmem.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/machine/asm.h b/arch/x86/machine/asm.h index a86fa1f..6118bb8 100644 --- a/arch/x86/machine/asm.h +++ b/arch/x86/machine/asm.h @@ -19,7 +19,7 @@ #define _X86_ASM_H #ifndef __ASSEMBLER__ -#error asm.h included from a C file +#warning "asm.h included from a C file" #endif /* __ASSEMBLER__ */ #include diff --git a/kern/rdxtree.c b/kern/rdxtree.c index 87fbcfd..788f3ba 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -49,7 +49,7 @@ typedef unsigned long rdxtree_bm_t; typedef unsigned long long rdxtree_bm_t; #define rdxtree_ffs(x) __builtin_ffsll(x) #else /* RDXTREE_RADIX < 6 */ -#error radix too high +#error "radix too high" #endif /* RDXTREE_RADIX < 6 */ /* diff --git a/kern/sleepq.c b/kern/sleepq.c index c494653..09548db 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -54,7 +54,7 @@ struct sleepq_waiter { #define SLEEPQ_COND_HTABLE_SIZE 64 #if !ISP2(SLEEPQ_HTABLE_SIZE) || !ISP2(SLEEPQ_COND_HTABLE_SIZE) -#error hash table size must be a power of two +#error "hash table size must be a power of two" #endif /* !ISP2(SLEEPQ_HTABLE_SIZE) */ #define SLEEPQ_HTABLE_MASK (SLEEPQ_HTABLE_SIZE - 1) diff --git a/kern/spinlock.c b/kern/spinlock.c index 0e6f2fa..8f42e9c 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -92,11 +92,11 @@ #define SPINLOCK_QID_LOCKED SPINLOCK_LOCKED #if SPINLOCK_QID_BITS > SPINLOCK_QID_MAX_BITS -#error spinlock qid too large +#error "spinlock qid too large" #endif #if X15_MAX_CPUS > (1 << SPINLOCK_QID_CPU_BITS) -#error maximum number of supported processors too large +#error "maximum number of supported processors too large" #endif struct spinlock_qnode { @@ -111,11 +111,11 @@ struct spinlock_qnode { #define SPINLOCK_NR_CTXS 4 #if SPINLOCK_CTX_INVALID != 0 -#error the invalid context value must be 0 +#error "the invalid context value must be 0" #endif #if SPINLOCK_NR_CTXS > SPINLOCK_QID_CTX_MAX -#error maximum number of contexts too large +#error "maximum number of contexts too large" #endif struct spinlock_cpu_data { diff --git a/kern/turnstile.c b/kern/turnstile.c index f5e94db..7b27d84 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -112,7 +112,7 @@ struct turnstile_waiter { #define TURNSTILE_HTABLE_SIZE 128 #if !ISP2(TURNSTILE_HTABLE_SIZE) -#error hash table size must be a power of two +#error "hash table size must be a power of two" #endif /* !ISP2(TURNSTILE_HTABLE_SIZE) */ #define TURNSTILE_HTABLE_MASK (TURNSTILE_HTABLE_SIZE - 1) diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index 615ff59..c4b5295 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -27,7 +27,7 @@ * report allocation errors. */ #if VM_MIN_KMEM_ADDRESS == 0 -#error kernel space must not start at address 0 +#error "kernel space must not start at address 0" #endif /* VM_MIN_KMEM_ADDRESS == 0 */ /* -- cgit v1.2.3 From a0563b4eec601eadc89081eeb7e14edd193ef95d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:58:33 +0200 Subject: kern/{,s}printf: guard against direct inclusion --- kern/printf.h | 4 ++++ kern/sprintf.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/kern/printf.h b/kern/printf.h index efaf2f9..16a7b58 100644 --- a/kern/printf.h +++ b/kern/printf.h @@ -27,6 +27,10 @@ #ifndef _KERN_PRINTK_H #define _KERN_PRINTK_H +#ifndef _STDIO_H +#error "do not use directly; include instead" +#endif /* _STDIO_H */ + #include #include diff --git a/kern/sprintf.h b/kern/sprintf.h index 0c09795..0c3fb83 100644 --- a/kern/sprintf.h +++ b/kern/sprintf.h @@ -28,6 +28,9 @@ #ifndef _KERN_SPRINTF_H #define _KERN_SPRINTF_H +#ifndef _STDIO_H +#error "do not use directly; include instead" +#endif /* _STDIO_H */ #include #include -- cgit v1.2.3 From 224179f50dfeb4ba2598505e4c9fcf524639439d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 22:59:14 +0200 Subject: kern/printf: complete renaming from printk to printf --- kern/printf.c | 4 ++-- kern/printf.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kern/printf.c b/kern/printf.c index 7e48b5d..68ae103 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -23,14 +23,14 @@ /* * Size of the static buffer. */ -#define PRINTK_BUFSIZE 1024 +#define PRINTF_BUFSIZE 1024 /* * XXX Must be provided by a console driver. */ extern void console_write_byte(char c); -static char printf_buffer[PRINTK_BUFSIZE]; +static char printf_buffer[PRINTF_BUFSIZE]; static struct spinlock printf_lock; int diff --git a/kern/printf.h b/kern/printf.h index 16a7b58..6643d66 100644 --- a/kern/printf.h +++ b/kern/printf.h @@ -24,8 +24,8 @@ * See the sprintf module for information about the supported formats. */ -#ifndef _KERN_PRINTK_H -#define _KERN_PRINTK_H +#ifndef _KERN_PRINTF_H +#define _KERN_PRINTF_H #ifndef _STDIO_H #error "do not use directly; include instead" @@ -39,4 +39,4 @@ int printf(const char *format, ...) __format_printf(1, 2); int vprintf(const char *format, va_list ap) __format_printf(1, 0); void printf_setup(void); -#endif /* _KERN_PRINTK_H */ +#endif /* _KERN_PRINTF_H */ -- cgit v1.2.3 From 2ccf7b707f025398ec4add30bcd1b998589e9f6e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 23:13:57 +0200 Subject: kern/sprintf: minor change Add newline separator. --- kern/sprintf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/kern/sprintf.h b/kern/sprintf.h index 0c3fb83..1cb51e7 100644 --- a/kern/sprintf.h +++ b/kern/sprintf.h @@ -31,6 +31,7 @@ #ifndef _STDIO_H #error "do not use directly; include instead" #endif /* _STDIO_H */ + #include #include -- cgit v1.2.3 From 52142d9dfa885b944fc3a3835cf5a2894463339d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Apr 2017 23:19:44 +0200 Subject: Fix custom autoconf macro synopsis format --- tools/m4/ax_prog_a2x.m4 | 2 +- tools/m4/ax_prog_asciidoc.m4 | 2 +- tools/m4/ax_prog_highlight.m4 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/m4/ax_prog_a2x.m4 b/tools/m4/ax_prog_a2x.m4 index 5695003..54a2f42 100644 --- a/tools/m4/ax_prog_a2x.m4 +++ b/tools/m4/ax_prog_a2x.m4 @@ -1,6 +1,6 @@ # SYNOPSIS # -# AX_PROG_A2X([ACTION-IF-NOT-FOUND]) +# AX_PROG_A2X(ACTION-IF-NOT-FOUND) # # DESCRIPTION # diff --git a/tools/m4/ax_prog_asciidoc.m4 b/tools/m4/ax_prog_asciidoc.m4 index b251cb6..6ca3421 100644 --- a/tools/m4/ax_prog_asciidoc.m4 +++ b/tools/m4/ax_prog_asciidoc.m4 @@ -1,6 +1,6 @@ # SYNOPSIS # -# AX_PROG_ASCIIDOC([ACTION-IF-NOT-FOUND]) +# AX_PROG_ASCIIDOC(ACTION-IF-NOT-FOUND) # # DESCRIPTION # diff --git a/tools/m4/ax_prog_highlight.m4 b/tools/m4/ax_prog_highlight.m4 index 647c628..4b49738 100644 --- a/tools/m4/ax_prog_highlight.m4 +++ b/tools/m4/ax_prog_highlight.m4 @@ -1,6 +1,6 @@ # SYNOPSIS # -# AX_PROG_HIGHLIGHT([ACTION-IF-NOT-FOUND]) +# AX_PROG_HIGHLIGHT(ACTION-IF-NOT-FOUND) # # DESCRIPTION # -- cgit v1.2.3 From b5864f82b9c0d8ea2e825e7d86bd8cdeb825f870 Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Thu, 23 Mar 2017 20:14:20 +0100 Subject: x86/cpu: test for cmpxchg8b feature in 32 bits mode --- arch/x86/machine/cpu.c | 12 ++++++++++++ arch/x86/machine/cpu.h | 1 + 2 files changed, 13 insertions(+) diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index ff7b722..261e586 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -538,6 +538,18 @@ cpu_check(const struct cpu *cpu) if (!(cpu->features2 & CPU_FEATURE2_APIC)) { cpu_panic_on_missing_feature("apic"); } + + /* + * The compiler is expected to produce cmpxchg8b instructions to + * perform 64-bits atomic operations on a 32-bits processor. Clang + * currently has trouble doing that so 64-bits atomic support is + * just disabled when building with it. + */ +#if !defined(__LP64__) && !defined(__clang__) + if (!(cpu->features2 & CPU_FEATURE2_CX8)) { + cpu_panic_on_missing_feature("cx8"); + } +#endif } void diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 862799e..3bf1ade 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -69,6 +69,7 @@ #define CPU_FEATURE2_PSE 0x00000008 #define CPU_FEATURE2_PAE 0x00000040 #define CPU_FEATURE2_MSR 0x00000020 +#define CPU_FEATURE2_CX8 0x00000100 #define CPU_FEATURE2_APIC 0x00000200 #define CPU_FEATURE2_PGE 0x00002000 -- cgit v1.2.3 From bcbe8dc991a9ec63f30d50ed4cca89fd9e6b7ff7 Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Fri, 5 May 2017 04:01:13 -1000 Subject: Replace sequential consistency with more efficient orders --- kern/bitmap.h | 4 ++-- kern/mutex.c | 4 ++-- kern/mutex_i.h | 4 ++-- kern/rtmutex.c | 8 ++++---- kern/rtmutex_i.h | 4 ++-- kern/semaphore_i.h | 4 ++-- kern/spinlock.c | 6 +++--- kern/spinlock_i.h | 4 ++-- kern/sref.c | 8 ++++---- kern/syscnt.h | 2 +- kern/thread.c | 2 +- kern/thread.h | 4 ++-- kern/thread_i.h | 4 ++-- 13 files changed, 29 insertions(+), 29 deletions(-) diff --git a/kern/bitmap.h b/kern/bitmap.h index 6b7f2d7..a10fb51 100644 --- a/kern/bitmap.h +++ b/kern/bitmap.h @@ -78,7 +78,7 @@ bitmap_set_atomic(unsigned long *bm, int bit) bitmap_lookup(bm, bit); } - atomic_or(bm, bitmap_mask(bit), ATOMIC_SEQ_CST); + atomic_or_acq_rel(bm, bitmap_mask(bit)); } static inline void @@ -98,7 +98,7 @@ bitmap_clear_atomic(unsigned long *bm, int bit) bitmap_lookup(bm, bit); } - atomic_and(bm, ~bitmap_mask(bit), ATOMIC_SEQ_CST); + atomic_and_acq_rel(bm, ~bitmap_mask(bit)); } static inline int diff --git a/kern/mutex.c b/kern/mutex.c index 00077f2..7899bef 100644 --- a/kern/mutex.c +++ b/kern/mutex.c @@ -34,7 +34,7 @@ mutex_lock_slow(struct mutex *mutex) sleepq = sleepq_lend(mutex, false, &flags); for (;;) { - state = atomic_swap_seq_cst(&mutex->state, MUTEX_CONTENDED); + state = atomic_swap_acquire(&mutex->state, MUTEX_CONTENDED); if (state == MUTEX_UNLOCKED) { break; @@ -44,7 +44,7 @@ mutex_lock_slow(struct mutex *mutex) } if (sleepq_empty(sleepq)) { - state = atomic_swap_seq_cst(&mutex->state, MUTEX_LOCKED); + state = atomic_swap_acquire(&mutex->state, MUTEX_LOCKED); assert(state == MUTEX_CONTENDED); } diff --git a/kern/mutex_i.h b/kern/mutex_i.h index d80cab2..a4a40eb 100644 --- a/kern/mutex_i.h +++ b/kern/mutex_i.h @@ -31,7 +31,7 @@ static inline unsigned int mutex_lock_fast(struct mutex *mutex) { - return atomic_cas_seq_cst(&mutex->state, MUTEX_UNLOCKED, MUTEX_LOCKED); + return atomic_cas_acquire(&mutex->state, MUTEX_UNLOCKED, MUTEX_LOCKED); } static inline unsigned int @@ -39,7 +39,7 @@ mutex_unlock_fast(struct mutex *mutex) { unsigned int state; - state = atomic_swap_seq_cst(&mutex->state, MUTEX_UNLOCKED); + state = atomic_swap_release(&mutex->state, MUTEX_UNLOCKED); assert((state == MUTEX_LOCKED) || (state == MUTEX_CONTENDED)); return state; } diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 3f4251f..6f639dd 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -29,7 +29,7 @@ static void rtmutex_set_contended(struct rtmutex *rtmutex) { - atomic_or(&rtmutex->owner, RTMUTEX_CONTENDED, ATOMIC_SEQ_CST); + atomic_or_acq_rel(&rtmutex->owner, RTMUTEX_CONTENDED); } void @@ -49,7 +49,7 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) bits = RTMUTEX_CONTENDED; for (;;) { - prev_owner = atomic_cas_seq_cst(&rtmutex->owner, bits, owner | bits); + prev_owner = atomic_cas_acquire(&rtmutex->owner, bits, owner | bits); assert((prev_owner & bits) == bits); if (prev_owner == bits) { @@ -64,7 +64,7 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) turnstile_own(turnstile); if (turnstile_empty(turnstile)) { - prev_owner = atomic_swap_seq_cst(&rtmutex->owner, owner); + prev_owner = atomic_swap_acquire(&rtmutex->owner, owner); assert(prev_owner == (owner | bits)); } @@ -89,7 +89,7 @@ rtmutex_unlock_slow(struct rtmutex *rtmutex) turnstile = turnstile_acquire(rtmutex); assert(turnstile != NULL); - prev_owner = atomic_swap_seq_cst(&rtmutex->owner, + prev_owner = atomic_swap_release(&rtmutex->owner, RTMUTEX_FORCE_WAIT | RTMUTEX_CONTENDED); assert((prev_owner & RTMUTEX_OWNER_MASK) == owner); diff --git a/kern/rtmutex_i.h b/kern/rtmutex_i.h index d34fb5f..2f2cc17 100644 --- a/kern/rtmutex_i.h +++ b/kern/rtmutex_i.h @@ -57,7 +57,7 @@ rtmutex_lock_fast(struct rtmutex *rtmutex) owner = (uintptr_t)thread_self(); rtmutex_assert_owner_aligned(owner); - return atomic_cas_seq_cst(&rtmutex->owner, 0, owner); + return atomic_cas_acquire(&rtmutex->owner, 0, owner); } static inline uintptr_t @@ -67,7 +67,7 @@ rtmutex_unlock_fast(struct rtmutex *rtmutex) owner = (uintptr_t)thread_self(); rtmutex_assert_owner_aligned(owner); - prev_owner = atomic_cas_seq_cst(&rtmutex->owner, owner, 0); + prev_owner = atomic_cas_release(&rtmutex->owner, owner, 0); assert((prev_owner & RTMUTEX_OWNER_MASK) == owner); return prev_owner; } diff --git a/kern/semaphore_i.h b/kern/semaphore_i.h index 9aa68e0..5498506 100644 --- a/kern/semaphore_i.h +++ b/kern/semaphore_i.h @@ -37,7 +37,7 @@ semaphore_dec(struct semaphore *semaphore) break; } - prev = atomic_cas_seq_cst(&semaphore->value, value, value - 1); + prev = atomic_cas_acquire(&semaphore->value, value, value - 1); } while (prev != value); return value; @@ -48,7 +48,7 @@ semaphore_inc(struct semaphore *semaphore) { unsigned int prev; - prev = atomic_fetch_add(&semaphore->value, 1, ATOMIC_SEQ_CST); + prev = atomic_fetch_add(&semaphore->value, 1, ATOMIC_RELEASE); assert(prev != SEMAPHORE_VALUE_MAX); return prev; } diff --git a/kern/spinlock.c b/kern/spinlock.c index 8f42e9c..f341406 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -196,7 +196,7 @@ spinlock_store_first_qid(struct spinlock *lock, unsigned int newqid) do { oldval = read_once(lock->value); newval = newqid | (oldval & SPINLOCK_QID_MASK); - prev = atomic_cas_seq_cst(&lock->value, oldval, newval); + prev = atomic_cas_acquire(&lock->value, oldval, newval); } while (prev != oldval); } @@ -220,7 +220,7 @@ spinlock_swap_last_qid(struct spinlock *lock, unsigned int newqid) oldval = read_once(lock->value); newval = (oldval & (SPINLOCK_QID_MASK << SPINLOCK_QID_MAX_BITS)) | newqid; - prev = atomic_cas_seq_cst(&lock->value, oldval, newval); + prev = atomic_cas_acquire(&lock->value, oldval, newval); } while (prev != oldval); return prev & SPINLOCK_QID_MASK; @@ -231,7 +231,7 @@ spinlock_try_downgrade(struct spinlock *lock, unsigned int oldqid) { unsigned int prev; - prev = atomic_cas_seq_cst(&lock->value, oldqid, SPINLOCK_QID_LOCKED); + prev = atomic_cas_acquire(&lock->value, oldqid, SPINLOCK_QID_LOCKED); assert((prev >> SPINLOCK_QID_MAX_BITS) == 0); assert(prev != SPINLOCK_QID_NULL); diff --git a/kern/spinlock_i.h b/kern/spinlock_i.h index 444ced9..4501803 100644 --- a/kern/spinlock_i.h +++ b/kern/spinlock_i.h @@ -41,7 +41,7 @@ spinlock_lock_fast(struct spinlock *lock) { unsigned int prev; - prev = atomic_cas_seq_cst(&lock->value, SPINLOCK_UNLOCKED, SPINLOCK_LOCKED); + prev = atomic_cas_acquire(&lock->value, SPINLOCK_UNLOCKED, SPINLOCK_LOCKED); if (unlikely(prev != SPINLOCK_UNLOCKED)) { return ERROR_BUSY; @@ -55,7 +55,7 @@ spinlock_unlock_fast(struct spinlock *lock) { unsigned int prev; - prev = atomic_cas_seq_cst(&lock->value, SPINLOCK_LOCKED, SPINLOCK_UNLOCKED); + prev = atomic_cas_release(&lock->value, SPINLOCK_LOCKED, SPINLOCK_UNLOCKED); if (unlikely(prev != SPINLOCK_LOCKED)) { return ERROR_BUSY; diff --git a/kern/sref.c b/kern/sref.c index 9ce7a83..3f399a3 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -259,13 +259,13 @@ sref_weakref_init(struct sref_weakref *weakref, struct sref_counter *counter) static void sref_weakref_mark_dying(struct sref_weakref *weakref) { - atomic_or(&weakref->addr, SREF_WEAKREF_DYING, ATOMIC_SEQ_CST); + atomic_or_acq_rel(&weakref->addr, SREF_WEAKREF_DYING); } static void sref_weakref_clear_dying(struct sref_weakref *weakref) { - atomic_and(&weakref->addr, SREF_WEAKREF_MASK, ATOMIC_SEQ_CST); + atomic_and_acq_rel(&weakref->addr, SREF_WEAKREF_MASK); } static int @@ -274,7 +274,7 @@ sref_weakref_kill(struct sref_weakref *weakref) uintptr_t addr, oldval; addr = weakref->addr | SREF_WEAKREF_DYING; - oldval = atomic_cas_seq_cst(&weakref->addr, addr, (uintptr_t)NULL); + oldval = atomic_cas_release(&weakref->addr, addr, (uintptr_t)NULL); if (oldval != addr) { assert((oldval & SREF_WEAKREF_MASK) == (addr & SREF_WEAKREF_MASK)); @@ -292,7 +292,7 @@ sref_weakref_tryget(struct sref_weakref *weakref) do { addr = weakref->addr; newval = addr & SREF_WEAKREF_MASK; - oldval = atomic_cas_seq_cst(&weakref->addr, addr, newval); + oldval = atomic_cas_acquire(&weakref->addr, addr, newval); } while (oldval != addr); return (struct sref_counter *)newval; diff --git a/kern/syscnt.h b/kern/syscnt.h index c28f3b5..a4bae3d 100644 --- a/kern/syscnt.h +++ b/kern/syscnt.h @@ -62,7 +62,7 @@ void syscnt_register(struct syscnt *syscnt, const char *name); static inline void syscnt_add(struct syscnt *syscnt, int64_t delta) { - atomic_add(&syscnt->value, delta, ATOMIC_SEQ_CST); + atomic_add(&syscnt->value, delta, ATOMIC_RELAXED); } static inline uint64_t diff --git a/kern/thread.c b/kern/thread.c index 42150e6..12d4245 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2724,7 +2724,7 @@ thread_key_create(unsigned int *keyp, thread_dtor_fn_t dtor) { unsigned int key; - key = atomic_fetch_add(&thread_nr_keys, 1, ATOMIC_SEQ_CST); + key = atomic_fetch_add(&thread_nr_keys, 1, ATOMIC_RELAXED); if (key >= THREAD_KEYS_MAX) { panic("thread: maximum number of keys exceeded"); diff --git a/kern/thread.h b/kern/thread.h index 3fa2a1f..ee58c43 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -270,7 +270,7 @@ thread_ref(struct thread *thread) { unsigned long nr_refs; - nr_refs = atomic_fetch_add(&thread->nr_refs, 1, ATOMIC_SEQ_CST); + nr_refs = atomic_fetch_add(&thread->nr_refs, 1, ATOMIC_RELAXED); assert(nr_refs != (unsigned long)-1); } @@ -279,7 +279,7 @@ thread_unref(struct thread *thread) { unsigned long nr_refs; - nr_refs = atomic_fetch_sub(&thread->nr_refs, 1, ATOMIC_SEQ_CST); + nr_refs = atomic_fetch_sub_acq_rel(&thread->nr_refs, 1); assert(nr_refs != 0); if (nr_refs == 1) { diff --git a/kern/thread_i.h b/kern/thread_i.h index 34761e5..2e1b88a 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -195,13 +195,13 @@ void thread_destroy(struct thread *thread); static inline void thread_set_flag(struct thread *thread, unsigned long flag) { - atomic_or(&thread->flags, flag, ATOMIC_SEQ_CST); + atomic_or_acq_rel(&thread->flags, flag); } static inline void thread_clear_flag(struct thread *thread, unsigned long flag) { - atomic_and(&thread->flags, ~flag, ATOMIC_SEQ_CST); + atomic_and_acq_rel(&thread->flags, ~flag); } static inline int -- cgit v1.2.3 From 102e667ea89896e52e7656234c20d665b09b35be Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 21:40:59 +0200 Subject: kern/atomic: add the consume memory order --- kern/atomic.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kern/atomic.h b/kern/atomic.h index 67f775d..9e26ad5 100644 --- a/kern/atomic.h +++ b/kern/atomic.h @@ -28,8 +28,20 @@ /* * Supported memory orders. + * + * Note that the consume order is aliased to relaxed. This assumes that + * all supported processors respect data dependencies. The rationale is + * that the definition for the consume order is confusing enough that + * most compilers alias it to acquire, which forces the generation of + * memory barrier instructions even when they're not really needed. + * Since there is currently no processor where using consume or relaxed + * would produce different code, it is safe to establish that alias. + * It serves as explicit documentation for code review, and will easily + * be replaced with the true consume order once compiler support becomes + * efficient and reliable. */ #define ATOMIC_RELAXED __ATOMIC_RELAXED +#define ATOMIC_CONSUME __ATOMIC_RELAXED #define ATOMIC_ACQUIRE __ATOMIC_ACQUIRE #define ATOMIC_RELEASE __ATOMIC_RELEASE #define ATOMIC_ACQ_REL __ATOMIC_ACQ_REL -- cgit v1.2.3 From 499f3502882fa63595bc9016fd9cd4382d880e30 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 21:53:11 +0200 Subject: kern/llsync: add atomic semantics to pointer accessors --- kern/llsync.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/kern/llsync.h b/kern/llsync.h index d269f2d..e95ed8c 100644 --- a/kern/llsync.h +++ b/kern/llsync.h @@ -72,6 +72,7 @@ #include +#include #include #include #include @@ -81,18 +82,12 @@ /* * Safely assign a pointer. */ -#define llsync_assign_ptr(ptr, value) \ -MACRO_BEGIN \ - mb_store(); \ - (ptr) = (value); \ -MACRO_END +#define llsync_assign_ptr(ptr, value) atomic_store(&(ptr), value, ATOMIC_RELEASE) /* * Safely access a pointer. - * - * No memory barrier, rely on data dependency to enforce ordering. */ -#define llsync_read_ptr(ptr) (ptr) +#define llsync_read_ptr(ptr) atomic_load(&(ptr), ATOMIC_CONSUME) /* * Read-side critical section enter/exit functions. -- cgit v1.2.3 From 697959897a860d0de156d277d423941271d8c978 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 10 May 2017 00:03:03 +0200 Subject: x86/atomic: fix architecture-specific atomic_store macro --- arch/x86/machine/atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/atomic.h b/arch/x86/machine/atomic.h index b5a0b5c..db49ad1 100644 --- a/arch/x86/machine/atomic.h +++ b/arch/x86/machine/atomic.h @@ -58,7 +58,7 @@ MACRO_END #define atomic_store(ptr, val, mo) \ MACRO_BEGIN \ - if (sizeof(*(ptr) != 8)) { \ + if (sizeof(*(ptr)) != 8) { \ __atomic_store_n(ptr, val, mo); \ } else { \ typeof(val) ___oval, ___nval; \ -- cgit v1.2.3 From 2d27404a4a93015e39575670d0489e73af55e628 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 10 May 2017 00:04:01 +0200 Subject: x86/atomic: work around qualifiers when loading with compare-and-swap --- arch/x86/machine/atomic.h | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/x86/machine/atomic.h b/arch/x86/machine/atomic.h index db49ad1..6844f32 100644 --- a/arch/x86/machine/atomic.h +++ b/arch/x86/machine/atomic.h @@ -27,6 +27,7 @@ #endif #include +#include #include @@ -41,21 +42,24 @@ * memory. Also note that this assumes the processor is at least an i586. */ -#define atomic_load(ptr, mo) \ +/* + * Temporarily discard qualifiers when loading 64-bits values with a + * compare-and-swap operation. + */ +#define atomic_load_64(ptr, mo) \ MACRO_BEGIN \ - typeof(*(ptr)) ___ret; \ - \ - if (sizeof(___ret) != 8) { \ - ___ret = __atomic_load_n(ptr, mo); \ - } else { \ - ___ret = 0; \ - __atomic_compare_exchange_n(ptr, &___ret, ___ret, \ - false, mo, __ATOMIC_RELAXED); \ - } \ + uint64_t ___ret; \ \ + __atomic_compare_exchange_n((uint64_t *)(ptr), &___ret, 0, \ + false, mo, __ATOMIC_RELAXED); \ ___ret; \ MACRO_END +#define atomic_load(ptr, mo) \ + (typeof(*(ptr)))__builtin_choose_expr(sizeof(*(ptr)) == 8, \ + atomic_load_64(ptr, mo), \ + __atomic_load_n(ptr, mo)) + #define atomic_store(ptr, val, mo) \ MACRO_BEGIN \ if (sizeof(*(ptr)) != 8) { \ -- cgit v1.2.3 From 54cd82044ae27059c2c8b995fb3f7fd3e17db795 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 22:03:05 +0200 Subject: kern/spinlock: use atomic accessors instead of memory barriers --- kern/spinlock.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kern/spinlock.c b/kern/spinlock.c index f341406..34b78ba 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -65,7 +65,6 @@ #include #include #include -#include #define SPINLOCK_QID_LOCK_BIT 1 @@ -268,8 +267,7 @@ spinlock_lock_slow(struct spinlock *lock) } for (;;) { - locked = read_once(qnode->locked); - mb_load(); + locked = atomic_load(&qnode->locked, ATOMIC_ACQUIRE); if (!locked) { break; @@ -316,7 +314,6 @@ spinlock_unlock_slow(struct spinlock *lock) cpu_pause(); } - mb_store(); next_qnode = spinlock_get_remote_qnode(next_qid); - write_once(next_qnode->locked, false); + atomic_store(&next_qnode->locked, false, ATOMIC_RELEASE); } -- cgit v1.2.3 From 7cab94c388ac03b724c5385f8cff05d96e1a55e9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 22:14:01 +0200 Subject: kern/xcall: use atomic accessors instead of memory barriers --- kern/xcall.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/kern/xcall.c b/kern/xcall.c index b3b01e2..364ad0b 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Richard Braun. + * Copyright (c) 2014-2017 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 @@ -18,13 +18,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include struct xcall { @@ -86,15 +86,21 @@ xcall_cpu_data_get_send_call(struct xcall_cpu_data *cpu_data, unsigned int cpu) } static struct xcall * -xcall_cpu_data_get_recv_call(struct xcall_cpu_data *cpu_data) +xcall_cpu_data_get_recv_call(const struct xcall_cpu_data *cpu_data) { - return cpu_data->recv_call; + return atomic_load(&cpu_data->recv_call, ATOMIC_ACQUIRE); +} + +static void +xcall_cpu_data_set_recv_call(struct xcall_cpu_data *cpu_data, struct xcall *call) +{ + atomic_store(&cpu_data->recv_call, call, ATOMIC_RELEASE); } static void xcall_cpu_data_clear_recv_call(struct xcall_cpu_data *cpu_data) { - cpu_data->recv_call = NULL; + xcall_cpu_data_set_recv_call(cpu_data, NULL); } void @@ -134,22 +140,16 @@ xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu) spinlock_lock(&remote_data->lock); - remote_data->recv_call = call; - - /* This barrier pairs with the one implied by the received IPI */ - mb_store(); + xcall_cpu_data_set_recv_call(remote_data, call); cpu_send_xcall(cpu); - while (remote_data->recv_call != NULL) { + while (xcall_cpu_data_get_recv_call(remote_data) != NULL) { cpu_pause(); } spinlock_unlock(&remote_data->lock); - /* This barrier pairs with the one in the interrupt handler */ - mb_load(); - out: thread_preempt_enable(); } @@ -165,6 +165,5 @@ xcall_intr(void) cpu_data = xcall_cpu_data_get(); call = xcall_cpu_data_get_recv_call(cpu_data); call->fn(call->arg); - mb_store(); xcall_cpu_data_clear_recv_call(cpu_data); } -- cgit v1.2.3 From f9af05807ab952a512619d94ec1fa260787b89c1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 22:15:23 +0200 Subject: kern/thread: remove memory barrier The flag operations already use atomic accessors, making the barrier irrelevant. --- kern/thread.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 12d4245..19032b2 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -110,7 +110,6 @@ #include #include #include -#include #include #include #include @@ -586,12 +585,6 @@ thread_runq_wakeup(struct thread_runq *runq, struct thread *thread) if ((runq != thread_runq_local()) && thread_test_flag(runq->current, THREAD_YIELD)) { - /* - * Make the new flags globally visible before sending the scheduling - * request. This barrier pairs with the one implied by the received IPI. - */ - mb_store(); - cpu_send_thread_schedule(thread_runq_cpu(runq)); } } -- cgit v1.2.3 From a1c5aab9aeefe17d0b17bcf7b2f9646c2567d061 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 9 May 2017 22:21:21 +0200 Subject: x86/mb: remove module The mb module has never been well defined, and has become irrelevant now that the kern/atomic module has been introduced. Phase it out until the need for non-SMP memory barriers rises. --- arch/x86/Makefrag.am | 1 - arch/x86/machine/cpu.c | 1 - arch/x86/machine/mb.h | 68 -------------------------------------------------- doc/intro.9.txt | 2 -- kern/llsync.h | 1 - 5 files changed, 73 deletions(-) delete mode 100644 arch/x86/machine/mb.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 5bedee0..eb140bc 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -43,7 +43,6 @@ x15_SOURCES += \ arch/x86/machine/io.h \ arch/x86/machine/lapic.c \ arch/x86/machine/lapic.h \ - arch/x86/machine/mb.h \ arch/x86/machine/multiboot.h \ arch/x86/machine/param.h \ arch/x86/machine/pic.c \ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 261e586..ff860ba 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/mb.h b/arch/x86/machine/mb.h deleted file mode 100644 index 9fbd74b..0000000 --- a/arch/x86/machine/mb.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2012, 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 3 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 . - * - * - * Memory barriers. - * - * Keep in mind that memory barriers only act on the ordering of loads and - * stores between internal processor queues and their caches. In particular, - * it doesn't imply a store is complete after the barrier has completed, only - * that other processors will see a new value thanks to the cache coherency - * protocol. Memory barriers alone aren't suitable for device communication. - * - * The x86 architectural memory model (total-store ordering) already enforces - * strong ordering for almost every access. The only exception is that stores - * can be reordered after loads. As a result, load and store memory barriers - * are simple compiler barriers whereas full memory barriers must generate - * a barrier instruction. - */ - -#ifndef _X86_MB_H -#define _X86_MB_H - -#include - -#ifdef __LP64__ - -static inline void -mb_sync(void) -{ - asm volatile("mfence" : : : "memory"); -} - -#else /* __LP64__ */ - -static inline void -mb_sync(void) -{ - asm volatile("lock addl $0, 0(%%esp)" : : : "memory"); -} - -#endif /* __LP64__ */ - -static inline void -mb_load(void) -{ - barrier(); -} - -static inline void -mb_store(void) -{ - barrier(); -} - -#endif /* _X86_MB_H */ diff --git a/doc/intro.9.txt b/doc/intro.9.txt index c53f65f..afac8e1 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -152,8 +152,6 @@ module:kern/atomic:: Inter-processor atomic operations. module:kern/cpumap:: Specialized bitmaps representing processor sets. -module:arch/mb:: - Inter-processor memory barriers. module:kern/percpu:: Per-processor data. module:kern/spinlock:: diff --git a/kern/llsync.h b/kern/llsync.h index e95ed8c..6736d6f 100644 --- a/kern/llsync.h +++ b/kern/llsync.h @@ -77,7 +77,6 @@ #include #include #include -#include /* * Safely assign a pointer. -- cgit v1.2.3 From 8b8522b33ff8e32ee88bee61e58b61d285a35b58 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 10 May 2017 00:11:03 +0200 Subject: x86/atomic: fix declaration of internal variables in atomic_store --- arch/x86/machine/atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/atomic.h b/arch/x86/machine/atomic.h index 6844f32..9f130a5 100644 --- a/arch/x86/machine/atomic.h +++ b/arch/x86/machine/atomic.h @@ -65,7 +65,7 @@ MACRO_BEGIN \ if (sizeof(*(ptr)) != 8) { \ __atomic_store_n(ptr, val, mo); \ } else { \ - typeof(val) ___oval, ___nval; \ + typeof(*(ptr)) ___oval, ___nval; \ bool ___done; \ \ ___oval = *(ptr); \ -- cgit v1.2.3 From d162c2add4aa6d81e010806b8d7936590c46b686 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 10 May 2017 01:04:37 +0200 Subject: Makefile.am: use standard bin path for kernel installation The path can be overriden with --bindir=, and this change makes distcheck work. --- Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index f7f0b09..7730313 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,8 +57,7 @@ SUFFIXES += .9 .9.html .9.xml .9.txt .9.xml.9: $(AM_V_GEN)$(A2X) $(A2X_FLAGS) -f manpage --xsltproc-opts="-o $(abs_top_builddir)/$@" $< -exec_bootdir = /boot -exec_boot_PROGRAMS = x15 +bin_PROGRAMS = x15 x15_DEPENDENCIES = arch/$(arch)/x15.lds MOSTLYCLEANFILES += arch/$(arch)/x15.lds x15_SOURCES = -- cgit v1.2.3 From 2d2672dd3729092336b25d955265ca6f46e440f7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 10 May 2017 01:07:54 +0200 Subject: doc/intro(9): completely remove mentions of the mb module --- doc/intro.9.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index afac8e1..57a5c58 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -216,8 +216,6 @@ module:arch/atomic:: Architecture-specific support for atomic instructions. module:arch/cpu:: Processor interface. -module:arch/mb:: - Memory barriers. module:arch/param:: Miscellaneous parameters. module:arch/pmap:: -- cgit v1.2.3 From 268e45101d5def9aeb053d19ae5c531a0f43f052 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 11 May 2017 06:30:46 +0200 Subject: kern/string: new strchr function --- kern/string.c | 18 +++++++++++++++++- kern/string.h | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/kern/string.c b/kern/string.c index 05218e0..b77aa2f 100644 --- a/kern/string.c +++ b/kern/string.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -176,3 +176,19 @@ strcmp(const char *s1, const char *s2) return (int)c1 - (int)c2; } #endif /* ARCH_STRING_STRCMP */ + +#ifndef ARCH_STRING_STRCHR +char * +strchr(const char *s, int c) +{ + for (;;) { + if (*s == c) { + return (char *)s; + } else if (*s == '\0') { + return NULL; + } + + s++; + } +} +#endif /* ARCH_STRING_STRCHR */ diff --git a/kern/string.h b/kern/string.h index 7ce4ab8..86786c2 100644 --- a/kern/string.h +++ b/kern/string.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -28,5 +28,6 @@ size_t strlen(const char *s); char * strcpy(char *dest, const char *src); size_t strlcpy(char *dest, const char *src, size_t n); int strcmp(const char *s1, const char *s2); +char * strchr(const char *s, int c); #endif /* _KERN_STRING_H */ -- cgit v1.2.3 From 3cf59e5e72669ced763b00659ab270c9b2436b4f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 11 May 2017 06:30:55 +0200 Subject: kern/arg: new module --- Makefrag.am | 2 + kern/arg.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/arg.h | 57 +++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 kern/arg.c create mode 100644 kern/arg.h diff --git a/Makefrag.am b/Makefrag.am index 90c146b..55bf93b 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -8,6 +8,8 @@ x15_SOURCES += \ include/string.h x15_SOURCES += \ + kern/arg.c \ + kern/arg.h \ kern/assert.h \ kern/atomic.h \ kern/bitmap.c \ diff --git a/kern/arg.c b/kern/arg.c new file mode 100644 index 0000000..02ed4b7 --- /dev/null +++ b/kern/arg.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017 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 3 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 + +/* + * Internally, all space characters are turned into null bytes so that + * values returned to callers directly point in the command line string, + * with no need for dynamic allocation. + */ +static char arg_cmdline[ARG_CMDLINE_MAX_SIZE] __initdata; +static const char *arg_cmdline_end __initdata; + +void __init +arg_setup(const char *cmdline) +{ + size_t i, length; + + if (cmdline == NULL) { + arg_cmdline[0] = '\0'; + return; + } + + length = strlen(cmdline); + + if ((length + 1) > ARRAY_SIZE(arg_cmdline)) { + panic("arg: command line too long"); + } + + memcpy(arg_cmdline, cmdline, length + 1); + + for (i = 0; i < length; i++) { + if (arg_cmdline[i] == ' ') { + arg_cmdline[i] = '\0'; + } + } + + arg_cmdline_end = arg_cmdline + length; +} + +void __init +arg_info(void) +{ + printf("arg: %s\n", arg_cmdline); +} + +static const char * __init +arg_walk(const char *s) +{ + if (s == NULL) { + s = arg_cmdline; + } else { + for (;;) { + if (s >= arg_cmdline_end) { + return NULL; + } + + if (*s == '\0') { + break; + } + + s++; + } + } + + for (;;) { + if (s >= arg_cmdline_end) { + return NULL; + } + + if (*s != '\0') { + return s; + } + + s++; + } +} + +static const char * __init +arg_find_name_end(const char *arg) +{ + const char *end; + + end = strchr(arg, '='); + + if (end == NULL) { + end = arg + strlen(arg); + } + + return end; +} + +static const char * __init +arg_find(const char *name) +{ + const char *arg, *arg_name_end; + size_t arg_name_length, name_length; + + name_length = strlen(name); + assert(name_length != 0); + + for (arg = arg_walk(NULL); arg != NULL; arg = arg_walk(arg)) { + arg_name_end = arg_find_name_end(arg); + arg_name_length = arg_name_end - arg; + + if ((arg_name_length == name_length) + && (memcmp(arg, name, name_length) == 0)) { + return arg; + } + } + + return NULL; +} + +bool __init +arg_present(const char *name) +{ + return (arg_find(name) != NULL); +} + +const char * __init +arg_value(const char *name) +{ + const char *arg, *value; + + arg = arg_find(name); + + if (arg == NULL) { + return NULL; + } + + value = strchr(arg, '='); + + if (value == NULL) { + return ""; + } + + return value + 1; +} diff --git a/kern/arg.h b/kern/arg.h new file mode 100644 index 0000000..937f596 --- /dev/null +++ b/kern/arg.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Kernel command line argument parsing. + * + * Arguments are separated by spaces (there is no escape character). + * They can be of the form "name" when used as boolean values (present + * or not), or "name=value". + */ + +#ifndef _KERN_ARG_H +#define _KERN_ARG_H + +#include + +#define ARG_CMDLINE_MAX_SIZE 256 + +/* + * Initialize the arg module. + */ +void arg_setup(const char *cmdline); + +/* + * Display command line information. + */ +void arg_info(void); + +/* + * Return true if an argument with the given name is present in the + * command line. + */ +bool arg_present(const char *name); + +/* + * Return the value of the argument with the given name in the command + * line. + * + * If the argument form is "name", the empty string is returned. If the + * argument isn't present, NULL is returned. + */ +const char * arg_value(const char *name); + +#endif /* _KERN_ARG_H */ -- cgit v1.2.3 From 27781cb2cbed5752861483623ac00e740a71678b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 11 May 2017 06:31:05 +0200 Subject: x86/boot: load kernel command line into the arg module --- arch/x86/machine/boot.c | 52 +++++++++++++++++++++++++++----------------- arch/x86/machine/multiboot.h | 1 - 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 011a319..b111a3c 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -48,6 +48,7 @@ #include #include +#include #include #include #include @@ -92,11 +93,15 @@ char boot_panic_long_mode_msg[] __bootdata static struct multiboot_raw_info boot_raw_mbi __bootdata; static struct multiboot_info boot_mbi __initdata; +static char boot_tmp_cmdline[ARG_CMDLINE_MAX_SIZE] __bootdata; + static char boot_panic_intro_msg[] __bootdata = "panic: "; static char boot_panic_loader_msg[] __bootdata = "boot: not started by a multiboot compliant boot loader"; static char boot_panic_meminfo_msg[] __bootdata = "boot: missing basic memory information"; +static char boot_panic_cmdline_msg[] __bootdata + = "boot: command line too long"; void * __boot boot_memcpy(void *dest, const void *src, size_t n) @@ -201,15 +206,11 @@ boot_panic(const char *msg) } static void __boot -boot_save_cmdline_sizes(struct multiboot_raw_info *mbi) +boot_save_mod_cmdline_sizes(struct multiboot_raw_info *mbi) { struct multiboot_raw_module *mod; uint32_t i; - if (mbi->flags & MULTIBOOT_LOADER_CMDLINE) { - mbi->unused0 = boot_strlen((char *)(uintptr_t)mbi->cmdline) + 1; - } - if (mbi->flags & MULTIBOOT_LOADER_MODULES) { uintptr_t addr; @@ -233,10 +234,6 @@ boot_register_data(const struct multiboot_raw_info *mbi) biosmem_register_boot_data((uintptr_t)&_boot, BOOT_VTOP((uintptr_t)&_end), false); - if ((mbi->flags & MULTIBOOT_LOADER_CMDLINE) && (mbi->cmdline != 0)) { - biosmem_register_boot_data(mbi->cmdline, mbi->cmdline + mbi->unused0, true); - } - if (mbi->flags & MULTIBOOT_LOADER_MODULES) { i = mbi->mods_count * sizeof(struct multiboot_raw_module); biosmem_register_boot_data(mbi->mods_addr, mbi->mods_addr + i, true); @@ -290,15 +287,36 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) */ boot_memmove(&boot_raw_mbi, mbi, sizeof(boot_raw_mbi)); + /* + * The kernel command line must be passed as early as possible to the + * arg module so that other modules can look up options. Instead of + * mapping it later, make a temporary copy. + */ + if (!(mbi->flags & MULTIBOOT_LOADER_CMDLINE)) { + boot_tmp_cmdline[0] = '\0'; + } else { + uintptr_t addr; + size_t length; + + addr = mbi->cmdline; + length = boot_strlen((const char *)addr) + 1; + + if (length > ARRAY_SIZE(boot_tmp_cmdline)) { + boot_panic(boot_panic_cmdline_msg); + } + + boot_memcpy(boot_tmp_cmdline, (const char *)addr, length); + } + if ((mbi->flags & MULTIBOOT_LOADER_MODULES) && (mbi->mods_count == 0)) { boot_raw_mbi.flags &= ~MULTIBOOT_LOADER_MODULES; } /* - * The kernel and modules command lines will be memory mapped later - * during initialization. Their respective sizes must be saved. + * The module command lines will be memory mapped later during + * initialization. Their respective sizes must be saved. */ - boot_save_cmdline_sizes(&boot_raw_mbi); + boot_save_mod_cmdline_sizes(&boot_raw_mbi); boot_register_data(&boot_raw_mbi); biosmem_bootstrap(&boot_raw_mbi); return pmap_setup_paging(); @@ -430,14 +448,6 @@ static void __init boot_save_data(void) { boot_mbi.flags = boot_raw_mbi.flags; - - if (boot_mbi.flags & MULTIBOOT_LOADER_CMDLINE) { - boot_mbi.cmdline = boot_save_memory(boot_raw_mbi.cmdline, - boot_raw_mbi.unused0); - } else { - boot_mbi.cmdline = NULL; - } - boot_save_mods(); strace_setup(&boot_raw_mbi); } @@ -445,6 +455,7 @@ boot_save_data(void) void __init boot_main(void) { + arg_setup(boot_tmp_cmdline); sleepq_bootstrap(); turnstile_bootstrap(); syscnt_setup(); @@ -455,6 +466,7 @@ boot_main(void) cga_setup(); printf_setup(); boot_show_version(); + arg_info(); pmap_bootstrap(); sref_bootstrap(); cpu_check(cpu_current()); diff --git a/arch/x86/machine/multiboot.h b/arch/x86/machine/multiboot.h index 02c8a33..80d793f 100644 --- a/arch/x86/machine/multiboot.h +++ b/arch/x86/machine/multiboot.h @@ -102,7 +102,6 @@ struct multiboot_module { struct multiboot_info { uint32_t flags; - char *cmdline; struct multiboot_module *mods_addr; uint32_t mods_count; }; -- cgit v1.2.3 From fca151d3e8b551483cc3f5d43394a1d90b79ee34 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 16 May 2017 01:57:40 +0200 Subject: x86/{cpu,pit}: implement cpu_delay using the timestamp counter --- arch/x86/machine/boot.c | 2 +- arch/x86/machine/cpu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/cpu.h | 19 ++++++++++++------- arch/x86/machine/pit.c | 8 +++++++- arch/x86/machine/pit.h | 11 ++++++++++- 5 files changed, 77 insertions(+), 10 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index b111a3c..09ed36e 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -461,6 +461,7 @@ boot_main(void) syscnt_setup(); percpu_bootstrap(); trap_setup(); + pit_setup_free_running(); cpu_setup(); thread_bootstrap(); cga_setup(); @@ -476,7 +477,6 @@ boot_main(void) boot_save_data(); biosmem_free_usable(); pic_setup(); - pit_setup(); cpu_mp_probe(); kernel_main(); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index ff860ba..d62b68f 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -34,10 +34,16 @@ #include #include #include +#include #include #include #include +/* + * Delay used for frequency measurement, in microseconds. + */ +#define CPU_FREQ_CAL_DELAY 1000000 + #define CPU_TYPE_MASK 0x00003000 #define CPU_TYPE_SHIFT 12 #define CPU_FAMILY_MASK 0x00000f00 @@ -140,6 +146,11 @@ struct cpu cpu_desc __percpu; */ unsigned int cpu_nr_active __read_mostly; +/* + * Processor frequency, assumed fixed and equal on all processors. + */ +static uint64_t cpu_freq __read_mostly; + /* * Interrupt descriptor table. */ @@ -154,6 +165,24 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; static unsigned long cpu_double_fault_handler; static char cpu_double_fault_stack[STACK_SIZE] __aligned(DATA_ALIGN); +void +cpu_delay(unsigned long usecs) +{ + int64_t total, prev, count, diff; + + assert(usecs != 0); + + total = DIV_CEIL((int64_t)usecs * cpu_freq, 1000000); + prev = cpu_get_tsc(); + + do { + count = cpu_get_tsc(); + diff = count - prev; + prev = count; + total -= diff; + } while (total > 0); +} + void * __init cpu_get_boot_stack(void) { @@ -508,6 +537,18 @@ cpu_init(struct cpu *cpu) cpu->state = CPU_STATE_ON; } +static void __init +cpu_measure_freq(void) +{ + uint64_t start, end; + + start = cpu_get_tsc(); + pit_delay(CPU_FREQ_CAL_DELAY); + end = cpu_get_tsc(); + + cpu_freq = (end - start) / (1000000 / CPU_FREQ_CAL_DELAY); +} + void __init cpu_setup(void) { @@ -518,6 +559,8 @@ cpu_setup(void) cpu->double_fault_stack = cpu_double_fault_stack; /* XXX */ cpu_init(cpu); cpu_nr_active = 1; + + cpu_measure_freq(); } static void __init @@ -566,6 +609,10 @@ cpu_info(const struct cpu *cpu) printf("cpu%u: address widths: physical: %hu, virtual: %hu\n", cpu->id, cpu->phys_addr_width, cpu->virt_addr_width); } + + printf("cpu%u: frequency: %llu.%02llu MHz\n", cpu->id, + (unsigned long long)cpu_freq / 1000000, + (unsigned long long)cpu_freq % 1000000); } void __init diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 3bf1ade..2360ea7 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Richard Braun. + * Copyright (c) 2010-2017 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 @@ -479,6 +479,15 @@ cpu_set_msr(uint32_t msr, uint32_t high, uint32_t low) asm volatile("wrmsr" : : "c" (msr), "a" (low), "d" (high)); } +static __always_inline uint64_t +cpu_get_tsc(void) +{ + uint32_t high, low; + + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + return ((uint64_t)high << 32) | low; +} + /* * Flush non-global TLB entries. * @@ -528,13 +537,9 @@ cpu_tlb_flush_va(unsigned long va) } /* - * XXX For now, directly use the PIT. + * Busy-wait for a given amount of time, in microseconds. */ -static inline void -cpu_delay(unsigned long usecs) -{ - pit_delay(usecs); -} +void cpu_delay(unsigned long usecs); /* * Return the address of the boot stack allocated for the current processor. diff --git a/arch/x86/machine/pit.c b/arch/x86/machine/pit.c index d3fece5..bf53ee1 100644 --- a/arch/x86/machine/pit.c +++ b/arch/x86/machine/pit.c @@ -45,7 +45,7 @@ #define PIT_MAX_COUNT 0xffff void __init -pit_setup(void) +pit_setup_free_running(void) { io_write_byte(PIT_PORT_MODE, PIT_MODE_RATE_GEN | PIT_MODE_RW_LSB | PIT_MODE_RW_MSB); @@ -53,6 +53,12 @@ pit_setup(void) io_write_byte(PIT_PORT_COUNTER0, PIT_MAX_COUNT >> 8); } +void __init +pit_setup(void) +{ + /* TODO Implement */ +} + static unsigned int pit_read(void) { diff --git a/arch/x86/machine/pit.h b/arch/x86/machine/pit.h index 3b4da60..7bf7a7b 100644 --- a/arch/x86/machine/pit.h +++ b/arch/x86/machine/pit.h @@ -19,7 +19,16 @@ #define _X86_PIT_H /* - * Set up the i8253 Programmable Interval Timer. + * Initialize the PIT as a free running counter. + * + * This is used during early initialization to measure the frequency of + * other clocks. The PIT is used despite its lack of precision because + * it's the only architectural timer with a known frequency. + */ +void pit_setup_free_running(void); + +/* + * Initialize the pit module. */ void pit_setup(void); -- cgit v1.2.3 From 3716fa334dc17b6e3bd03493429877fceb606a1c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 16 May 2017 01:57:46 +0200 Subject: x86/lapic: reduce timer calibration delay Now that cpu_delay uses the timestamp counter, local APIC timer calibration gets more precise, and doesn't require such a long interval. --- arch/x86/machine/lapic.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 89affc5..a978da1 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -55,13 +55,9 @@ #define LAPIC_TIMER_COUNT_MAX 0xffffffff /* - * The value of this duration (in microseconds) must be carefully set. - * It must divide a second (1000000) without loss of precision. It is - * recommended to use either 1s or 100ms. The former gives the best - * results, as it renders the time used for accounting operations - * negligible, but is slightly longer. + * Delay used to calibrate the local APIC timer, in microseconds. */ -#define LAPIC_TIMER_CAL_DELAY 1000000 +#define LAPIC_TIMER_CAL_DELAY 100000 /* * Spurious-interrupt vector register bits. -- cgit v1.2.3 From 4087fd3abb6ed0672cc7e84d42ad226770542757 Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Tue, 16 May 2017 04:16:55 -1000 Subject: x86/trap: add the trap_handler_get helper function --- arch/x86/machine/trap.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 9a2fe15..c938f0f 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -90,6 +90,13 @@ void trap_isr_lapic_spurious(void); */ static struct trap_handler trap_handlers[CPU_IDT_SIZE + 1] __read_mostly; +static struct trap_handler * +trap_handler_get(unsigned int vector) +{ + assert(vector < ARRAY_SIZE(trap_handlers)); + return &trap_handlers[vector]; +} + static void __init trap_handler_init(struct trap_handler *handler, int flags, trap_handler_fn_t fn) { @@ -103,7 +110,7 @@ trap_install(unsigned int vector, int flags, trap_isr_fn_t isr, { assert(vector < CPU_IDT_SIZE); - trap_handler_init(&trap_handlers[vector], flags, fn); + trap_handler_init(trap_handler_get(vector), flags, fn); cpu_idt_set_gate(vector, isr); } @@ -162,7 +169,8 @@ trap_double_fault(struct trap_frame *frame) static void __init trap_install_double_fault(void) { - trap_handler_init(&trap_handlers[TRAP_DF], TRAP_HF_INTR, trap_double_fault); + trap_handler_init(trap_handler_get(TRAP_DF), + TRAP_HF_INTR, trap_double_fault); cpu_idt_set_double_fault(trap_isr_double_fault); } @@ -226,7 +234,8 @@ trap_setup(void) trap_install(TRAP_LAPIC_SPURIOUS, TRAP_HF_INTR, trap_isr_lapic_spurious, lapic_spurious_intr); - trap_handler_init(&trap_handlers[TRAP_DEFAULT], TRAP_HF_INTR, trap_default); + trap_handler_init(trap_handler_get(TRAP_DEFAULT), + TRAP_HF_INTR, trap_default); } void @@ -234,9 +243,9 @@ trap_main(struct trap_frame *frame) { struct trap_handler *handler; - assert(frame->vector < ARRAY_SIZE(trap_handlers)); + assert(!cpu_intr_enabled()); - handler = &trap_handlers[frame->vector]; + handler = trap_handler_get(frame->vector); if (handler->flags & TRAP_HF_INTR) { thread_intr_enter(); -- cgit v1.2.3 From e3b917f8995a5d8a0eef583443cae6faf151261f Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Tue, 16 May 2017 04:18:12 -1000 Subject: x86/trap: add interrupt stacks --- arch/x86/machine/trap.c | 24 +++++++++++++++++++++++- arch/x86/machine/trap.h | 5 +++++ arch/x86/machine/trap_asm.S | 26 +++++++++++++++++++++++--- kern/thread.c | 10 ++++++++++ kern/thread.h | 14 +------------- 5 files changed, 62 insertions(+), 17 deletions(-) diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index c938f0f..aa045a9 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -33,6 +33,12 @@ #include #include +struct trap_cpu_data { + unsigned char intr_stack[STACK_SIZE] __aligned(DATA_ALIGN); +}; + +static struct trap_cpu_data trap_cpu_data __percpu; + /* * Type for interrupt service routines and trap handler functions. */ @@ -257,7 +263,7 @@ trap_main(struct trap_frame *frame) thread_intr_leave(); } - thread_schedule(); + assert(!cpu_intr_enabled()); } #ifdef __LP64__ @@ -340,3 +346,19 @@ trap_stack_show(struct trap_frame *frame) strace_show(frame->eip, frame->ebp); #endif /* __LP64__ */ } + +void * +trap_get_interrupt_stack(const struct trap_frame *frame) +{ + struct trap_cpu_data *cpu_data; + struct trap_handler *handler; + + handler = trap_handler_get(frame->vector); + + if ((handler->flags & TRAP_HF_INTR) && !thread_interrupted()) { + cpu_data = cpu_local_ptr(trap_cpu_data); + return cpu_data->intr_stack + sizeof(cpu_data->intr_stack); + } else { + return NULL; + } +} diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index a652382..ea0edc3 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -151,6 +151,11 @@ void trap_frame_show(struct trap_frame *frame); */ void trap_stack_show(struct trap_frame *frame); +/* + * Return the interrupt stack for the current thread. + */ +void * trap_get_interrupt_stack(const struct trap_frame *frame); + #endif /* __ASSEMBLER__ */ #endif /* _X86_TRAP_H */ diff --git a/arch/x86/machine/trap_asm.S b/arch/x86/machine/trap_asm.S index 97f48a6..16d431c 100644 --- a/arch/x86/machine/trap_asm.S +++ b/arch/x86/machine/trap_asm.S @@ -74,8 +74,18 @@ ASM_END(name) ASM_ENTRY(trap_common) TRAP_STORE_REGISTERS - movq %rsp, %rdi + movq %rsp, %rbx /* save frame */ + movq %rbx, %rdi + call trap_get_interrupt_stack + testq %rax, %rax /* must we switch stacks? */ + jz 1f + movq %rax, %rsp /* switch to interrupt stack TODO conditional move ? */ +1: + xorq %rbp, %rbp /* block stack tracing */ + movq %rbx, %rdi call trap_main + movq %rbx, %rsp /* restore stack */ + call thread_schedule /* schedule threads */ TRAP_LOAD_REGISTERS iretq ASM_END(trap_common) @@ -123,9 +133,19 @@ ASM_END(name) ASM_ENTRY(trap_common) TRAP_STORE_REGISTERS - pushl %esp - call trap_main + movl %esp, %ebx /* save frame */ + pushl %ebx + call trap_get_interrupt_stack addl $4, %esp + testl %eax, %eax /* must we switch stacks? */ + jz 1f + movl %eax, %esp /* switch to interrupt stack */ +1: + xorl %ebp, %ebp /* block stack tracing */ + pushl %ebx /* push frame */ + call trap_main + movl %ebx, %esp /* restore stack */ + call thread_schedule /* schedule threads */ TRAP_LOAD_REGISTERS iret ASM_END(trap_common) diff --git a/kern/thread.c b/kern/thread.c index 19032b2..e687558 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2477,6 +2477,16 @@ thread_yield(void) } while (thread_test_flag(thread, THREAD_YIELD)); } +void +thread_schedule(void) +{ + if (likely(!thread_test_flag(thread_self(), THREAD_YIELD))) { + return; + } + + thread_yield(); +} + void thread_schedule_intr(void) { diff --git a/kern/thread.h b/kern/thread.h index ee58c43..6270826 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -394,20 +394,8 @@ thread_self(void) * Main scheduler invocation call. * * Called on return from interrupt or when reenabling preemption. - * - * Implies a compiler barrier. */ -static inline void -thread_schedule(void) -{ - barrier(); - - if (likely(!thread_test_flag(thread_self(), THREAD_YIELD))) { - return; - } - - thread_yield(); -} +void thread_schedule(void); /* * Sleep queue lending functions. -- cgit v1.2.3 From 8703a289f532a789d95cae7469055d202e160d73 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 18 May 2017 21:32:31 +0200 Subject: x86/trap: minor changes Edit/remove a few comments. --- arch/x86/machine/trap_asm.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/machine/trap_asm.S b/arch/x86/machine/trap_asm.S index 16d431c..15e8c78 100644 --- a/arch/x86/machine/trap_asm.S +++ b/arch/x86/machine/trap_asm.S @@ -77,7 +77,7 @@ ASM_ENTRY(trap_common) movq %rsp, %rbx /* save frame */ movq %rbx, %rdi call trap_get_interrupt_stack - testq %rax, %rax /* must we switch stacks? */ + testq %rax, %rax /* switch stack ? */ jz 1f movq %rax, %rsp /* switch to interrupt stack TODO conditional move ? */ 1: @@ -137,12 +137,12 @@ ASM_ENTRY(trap_common) pushl %ebx call trap_get_interrupt_stack addl $4, %esp - testl %eax, %eax /* must we switch stacks? */ + testl %eax, %eax /* switch stack ? */ jz 1f movl %eax, %esp /* switch to interrupt stack */ 1: xorl %ebp, %ebp /* block stack tracing */ - pushl %ebx /* push frame */ + pushl %ebx call trap_main movl %ebx, %esp /* restore stack */ call thread_schedule /* schedule threads */ -- cgit v1.2.3 From 3c203f5b93f23927a1e046f120a1a9438c5213bc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 19 May 2017 18:50:00 +0200 Subject: x86/trap: improve trap_get_interrupt_stack description --- arch/x86/machine/trap.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index ea0edc3..2f53f17 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -152,7 +152,11 @@ void trap_frame_show(struct trap_frame *frame); void trap_stack_show(struct trap_frame *frame); /* - * Return the interrupt stack for the current thread. + * Return a pointer to the local interrupt stack. + * + * This function is called by the low level trap handling code. + * + * Return NULL if no stack switching is required. */ void * trap_get_interrupt_stack(const struct trap_frame *frame); -- cgit v1.2.3 From 805ba95708f577ea3cfb59f9851ea61ef45abc85 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 19 May 2017 19:11:34 +0200 Subject: kern/console: new module --- Makefrag.am | 2 ++ arch/x86/machine/boot.c | 2 ++ arch/x86/machine/cga.c | 83 ++++++++++++++++++++++++++++--------------------- arch/x86/machine/cga.h | 7 ----- kern/console.c | 69 ++++++++++++++++++++++++++++++++++++++++ kern/console.h | 71 ++++++++++++++++++++++++++++++++++++++++++ kern/printf.c | 8 ++--- 7 files changed, 193 insertions(+), 49 deletions(-) create mode 100644 kern/console.c create mode 100644 kern/console.h diff --git a/Makefrag.am b/Makefrag.am index 55bf93b..8a7b8e9 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -18,6 +18,8 @@ x15_SOURCES += \ kern/condition.c \ kern/condition.h \ kern/condition_types.h \ + kern/console.c \ + kern/console.h \ kern/config.h \ kern/cpumap.c \ kern/cpumap.h \ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 09ed36e..28ed46e 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -464,6 +465,7 @@ boot_main(void) pit_setup_free_running(); cpu_setup(); thread_bootstrap(); + console_setup(); cga_setup(); printf_setup(); boot_show_version(); diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index c926ec3..86a661c 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -69,6 +70,8 @@ static uint8_t *cga_memory __read_mostly; static uint16_t cga_cursor; +static struct console cga_console; + static uint16_t cga_get_cursor_position(void) { @@ -101,33 +104,6 @@ cga_get_cursor_column(void) return cga_cursor % CGA_COLUMNS; } -void __init -cga_setup(void) -{ - uint8_t misc_output_register; - - cga_memory = (void *)vm_page_direct_va(CGA_MEMORY); - - /* - * Check if the Input/Output Address Select bit is set. - */ - misc_output_register = io_read_byte(CGA_MISC_OUTPUT_REGISTER_READ); - - if (!(misc_output_register & 0x1)) { - /* - * Set the I/O AS bit. - */ - misc_output_register |= 0x1; - - /* - * Update the misc output register. - */ - io_write_byte(CGA_MISC_OUTPUT_REGISTER_WRITE, misc_output_register); - } - - cga_cursor = cga_get_cursor_position(); -} - static void cga_scroll_lines(void) { @@ -143,12 +119,12 @@ cga_scroll_lines(void) } } -void -cga_write_byte(uint8_t byte) +static void +cga_write_char(char c) { - if (byte == '\r') { + if (c == '\r') { return; - } else if (byte == '\n') { + } else if (c == '\n') { cga_cursor += CGA_COLUMNS - cga_get_cursor_column(); if (cga_cursor >= (CGA_LINES * CGA_COLUMNS)) { @@ -157,17 +133,17 @@ cga_write_byte(uint8_t byte) } cga_set_cursor_position(cga_cursor); - } else if (byte == '\b') { + } else if (c == '\b') { if (cga_cursor > 0) { cga_cursor--; ((uint16_t *)cga_memory)[cga_cursor] = CGA_BLANK; cga_set_cursor_position(cga_cursor); } - } else if (byte == '\t') { + } else if (c == '\t') { int i; for(i = 0; i < CGA_TABULATION_SPACES; i++) { - cga_write_byte(' '); + cga_write_char(' '); } } else { if ((cga_cursor + 1) >= CGA_COLUMNS * CGA_LINES) { @@ -176,10 +152,45 @@ cga_write_byte(uint8_t byte) } ((uint16_t *)cga_memory)[cga_cursor] = ((CGA_FOREGROUND_COLOR << 8) - | byte); + | c); cga_cursor++; cga_set_cursor_position(cga_cursor); } } -void console_write_byte(char c) __alias("cga_write_byte"); +static void +cga_console_putc(struct console *console, char c) +{ + (void)console; + cga_write_char(c); +} + +void __init +cga_setup(void) +{ + uint8_t misc_output_register; + + cga_memory = (void *)vm_page_direct_va(CGA_MEMORY); + + /* + * Check if the Input/Output Address Select bit is set. + */ + misc_output_register = io_read_byte(CGA_MISC_OUTPUT_REGISTER_READ); + + if (!(misc_output_register & 0x1)) { + /* + * Set the I/O AS bit. + */ + misc_output_register |= 0x1; + + /* + * Update the misc output register. + */ + io_write_byte(CGA_MISC_OUTPUT_REGISTER_WRITE, misc_output_register); + } + + cga_cursor = cga_get_cursor_position(); + + console_init(&cga_console, cga_console_putc); + console_register(&cga_console); +} diff --git a/arch/x86/machine/cga.h b/arch/x86/machine/cga.h index 2dc12cf..a677162 100644 --- a/arch/x86/machine/cga.h +++ b/arch/x86/machine/cga.h @@ -21,16 +21,9 @@ #ifndef _X86_CGA_H #define _X86_CGA_H -#include - /* * Initialize the cga module. */ void cga_setup(void); -/* - * Write a byte on the screen at current cursor position. - */ -void cga_write_byte(uint8_t byte); - #endif /* _X86_CGA_H */ diff --git a/kern/console.c b/kern/console.c new file mode 100644 index 0000000..138a424 --- /dev/null +++ b/kern/console.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 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 3 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 + +static struct list console_devs; +static struct spinlock console_lock; + +static void +console_putc(struct console *console, char c) +{ + unsigned long flags; + + spinlock_lock_intr_save(&console->lock, &flags); + console->putc(console, c); + spinlock_unlock_intr_restore(&console->lock, flags); +} + +void __init +console_setup(void) +{ + list_init(&console_devs); + spinlock_init(&console_lock); +} + +void __init +console_register(struct console *console) +{ + assert(console->putc != NULL); + + spinlock_lock(&console_lock); + list_insert_tail(&console_devs, &console->node); + spinlock_unlock(&console_lock); +} + +void +console_write_char(char c) +{ + struct console *console; + unsigned long flags; + + spinlock_lock_intr_save(&console_lock, &flags); + + list_for_each_entry(&console_devs, console, node) { + console_putc(console, c); + } + + spinlock_unlock_intr_restore(&console_lock, flags); +} diff --git a/kern/console.h b/kern/console.h new file mode 100644 index 0000000..8fdef16 --- /dev/null +++ b/kern/console.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Device-independent console interface. + */ + +#ifndef _KERN_CONSOLE_H +#define _KERN_CONSOLE_H + +#include +#include + +struct console; + +/* + * Type for character writing functions. + */ +typedef void (*console_putc_fn)(struct console *console, char c); + +/* + * Console device. + * + * This structure should be embedded in the hardware-specific console + * objects. Calls to console operations are all serialized by this module + * for each device. + */ +struct console { + struct spinlock lock; + struct list node; + console_putc_fn putc; +}; + +static inline void +console_init(struct console *console, console_putc_fn putc) +{ + spinlock_init(&console->lock); + console->putc = putc; +} + +/* + * Initialize the console module. + */ +void console_setup(void); + +/* + * Register a console device. + * + * The given console must be initialized before calling this function. + */ +void console_register(struct console *console); + +/* + * Write a single character to all registered console devices. + */ +void console_write_char(char c); + +#endif /* _KERN_CONSOLE_H */ diff --git a/kern/printf.c b/kern/printf.c index 68ae103..d7a1d10 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -17,6 +17,7 @@ #include +#include #include #include @@ -25,11 +26,6 @@ */ #define PRINTF_BUFSIZE 1024 -/* - * XXX Must be provided by a console driver. - */ -extern void console_write_byte(char c); - static char printf_buffer[PRINTF_BUFSIZE]; static struct spinlock printf_lock; @@ -58,7 +54,7 @@ vprintf(const char *format, va_list ap) length = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); for (ptr = printf_buffer; *ptr != '\0'; ptr++) { - console_write_byte(*ptr); + console_write_char(*ptr); } spinlock_unlock_intr_restore(&printf_lock, flags); -- cgit v1.2.3 From 9772e159b4e3b22933f08487cb38d73f0f247c4c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 19 May 2017 19:31:30 +0200 Subject: x86/uart: new module --- arch/x86/Makefrag.am | 4 +- arch/x86/machine/biosmem.c | 13 +++ arch/x86/machine/biosmem.h | 14 +++ arch/x86/machine/boot.c | 3 + arch/x86/machine/uart.c | 216 +++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/uart.h | 34 +++++++ 6 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 arch/x86/machine/uart.c create mode 100644 arch/x86/machine/uart.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index eb140bc..d5f1583 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -60,6 +60,8 @@ x15_SOURCES += \ arch/x86/machine/trap_asm.S \ arch/x86/machine/trap.c \ arch/x86/machine/trap.h \ - arch/x86/machine/types.h + arch/x86/machine/types.h \ + arch/x86/machine/uart.c \ + arch/x86/machine/uart.h endif X86 diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 35e015b..65329a0 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -94,6 +94,11 @@ static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2] __bootdata; static unsigned int biosmem_map_size __bootdata; +/* + * Temporary copy of the BIOS Data Area. + */ +static char biosmem_bda[BIOSMEM_BDA_SIZE] __bootdata; + /* * Contiguous block of physical memory. */ @@ -649,6 +654,8 @@ biosmem_bootstrap(const struct multiboot_raw_info *mbi) phys_addr_t phys_start, phys_end; int error; + boot_memcpy(biosmem_bda, (const void *)BIOSMEM_BDA_ADDR, BIOSMEM_BDA_SIZE); + if (mbi->flags & MULTIBOOT_LOADER_MMAP) { biosmem_map_build(mbi); } else { @@ -738,6 +745,12 @@ biosmem_bootalloc(unsigned int nr_pages) return boot_memset((void *)addr, 0, size); } +const void * +biosmem_get_bda(void) +{ + return biosmem_bda; +} + phys_addr_t __boot biosmem_directmap_end(void) { diff --git a/arch/x86/machine/biosmem.h b/arch/x86/machine/biosmem.h index 19ec2cd..d877af4 100644 --- a/arch/x86/machine/biosmem.h +++ b/arch/x86/machine/biosmem.h @@ -23,6 +23,12 @@ #include #include +/* + * BIOS Data Area. + */ +#define BIOSMEM_BDA_ADDR 0x400 +#define BIOSMEM_BDA_SIZE 0x100 + /* * Address where the address of the Extended BIOS Data Area segment can be * found. @@ -82,6 +88,14 @@ void biosmem_bootstrap(const struct multiboot_raw_info *mbi); */ void * biosmem_bootalloc(unsigned int nr_pages); +/* + * Return a pointer to a copy of the BIOS Data Area. + * + * The region denoted by the returned address is of size BIOSMEM_BDA_SIZE, + * and is only valid during initialization. + */ +const void * biosmem_get_bda(void); + /* * Return the limit of physical memory that can be directly mapped. */ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 28ed46e..1830600 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include @@ -467,9 +468,11 @@ boot_main(void) thread_bootstrap(); console_setup(); cga_setup(); + uart_setup(); printf_setup(); boot_show_version(); arg_info(); + uart_info(); pmap_bootstrap(); sref_bootstrap(); cpu_check(cpu_current()); diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c new file mode 100644 index 0000000..6277ae1 --- /dev/null +++ b/arch/x86/machine/uart.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * TODO Make serial line parameters configurable. + * TODO 16550A support. + * TODO Interrupt probing and handling. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define UART_BDA_COM1_OFFSET 0 + +#define UART_REG_THR 0 +#define UART_REG_DLL 0 +#define UART_REG_IER 1 +#define UART_REG_DLH 1 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +#define UART_NR_REGS 6 + +#define UART_THR_EMPTY 0x20 + +#define UART_LCR_8BITS 0x03 +#define UART_LCR_1S 0x00 +#define UART_LCR_NP 0x00 +#define UART_LCR_BEN 0x40 +#define UART_LCR_DLAB 0x80 + +#define UART_MCR_DTR 0x01 +#define UART_MCR_RTS 0x02 +#define UART_MCR_AUX2 0x04 + +#define UART_MAX_DEVS 4 + +struct uart { + struct console console; + uint16_t port; + uint16_t intr; +}; + +static struct uart uart_devs[UART_MAX_DEVS]; + +static uint16_t uart_intrs[UART_MAX_DEVS] = { 4, 3, 4, 3 }; + +static uint16_t +uart_get_addr(const struct uart *uart, uint16_t reg) +{ + assert(reg < UART_NR_REGS); + return uart->port + reg; +} + +static uint8_t +uart_read(struct uart *uart, uint16_t reg) +{ + return io_read_byte(uart_get_addr(uart, reg)); +} + +static void +uart_write(struct uart *uart, uint16_t reg, uint8_t byte) +{ + io_write_byte(uart_get_addr(uart, reg), byte); +} + +static void +uart_set(struct uart *uart, uint16_t reg, uint8_t mask) +{ + uint16_t addr; + uint8_t byte; + + addr = uart_get_addr(uart, reg); + byte = io_read_byte(addr); + byte |= mask; + io_write_byte(addr, byte); +} + +static void +uart_clear(struct uart *uart, uint16_t reg, uint8_t mask) +{ + uint16_t addr; + uint8_t byte; + + addr = uart_get_addr(uart, reg); + byte = io_read_byte(addr); + byte &= ~mask; + io_write_byte(addr, byte); +} + +static void +uart_tx_wait(struct uart *uart) +{ + uint8_t byte; + + for (;;) { + byte = uart_read(uart, UART_REG_LSR); + + if (byte & UART_THR_EMPTY) { + break; + } + } +} + +static struct uart * +uart_get_dev(size_t i) +{ + assert(i < ARRAY_SIZE(uart_devs)); + return &uart_devs[i]; +} + +static struct uart * +uart_get_from_console(struct console *console) +{ + return structof(console, struct uart, console); +} + +static void +uart_write_char_common(struct uart *uart, char c) +{ + uart_tx_wait(uart); + uart_write(uart, UART_REG_THR, (uint8_t)c); +} + +static void +uart_write_char(struct uart *uart, char c) +{ + if (c == '\n') { + uart_write_char_common(uart, '\r'); + } + + uart_write_char_common(uart, c); +} + +static void +uart_console_putc(struct console *console, char c) +{ + uart_write_char(uart_get_from_console(console), c); +} + +static void __init +uart_init(struct uart *uart, uint16_t port, uint16_t intr) +{ + uint8_t byte; + + uart->port = port; + uart->intr = intr; + + uart_write(uart, UART_REG_IER, 0); + uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR); + + uart_set(uart, UART_REG_LCR, UART_LCR_DLAB); + uart_write(uart, UART_REG_DLH, 0); + uart_write(uart, UART_REG_DLL, 1); + uart_clear(uart, UART_REG_LCR, UART_LCR_DLAB); + + byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S | UART_LCR_BEN; + uart_write(uart, UART_REG_LCR, byte); + + console_init(&uart->console, uart_console_putc); + console_register(&uart->console); +} + +void __init +uart_setup(void) +{ + const uint16_t *ptr; + size_t i; + + ptr = biosmem_get_bda() + UART_BDA_COM1_OFFSET; + + for (i = 0; i < UART_MAX_DEVS; i++) { + if (ptr[i] == 0) { + continue; + } + + uart_init(uart_get_dev(i), ptr[i], uart_intrs[i]); + } +} + +void __init +uart_info(void) +{ + const struct uart *uart; + size_t i; + + for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { + uart = uart_get_dev(i); + + if (uart->port != 0) { + printf("uart%zu: port:%#x irq:%u\n", i, (unsigned int)uart->port, + (unsigned int)uart->intr); + } + } +} diff --git a/arch/x86/machine/uart.h b/arch/x86/machine/uart.h new file mode 100644 index 0000000..9cb7cbf --- /dev/null +++ b/arch/x86/machine/uart.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Tiny 8250 UART driver. + */ + +#ifndef _X86_UART_H +#define _X86_UART_H + +/* + * Initialize the uart module. + */ +void uart_setup(void); + +/* + * Display device information. + */ +void uart_info(void); + +#endif /* _X86_UART_H */ -- cgit v1.2.3 From 4fd4db00fcd59872106591863f321b44ed35740c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 May 2017 20:26:26 +0200 Subject: x86/uart: minor rename --- arch/x86/machine/uart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 6277ae1..d70cc41 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -33,7 +33,7 @@ #define UART_BDA_COM1_OFFSET 0 -#define UART_REG_THR 0 +#define UART_REG_DAT 0 #define UART_REG_DLL 0 #define UART_REG_IER 1 #define UART_REG_DLH 1 @@ -140,7 +140,7 @@ static void uart_write_char_common(struct uart *uart, char c) { uart_tx_wait(uart); - uart_write(uart, UART_REG_THR, (uint8_t)c); + uart_write(uart, UART_REG_DAT, (uint8_t)c); } static void -- cgit v1.2.3 From ea5c6b46ca704a05186f18bfe1ae9cd986806150 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 May 2017 20:28:06 +0200 Subject: x86/cpu: new cpu_apic_id function --- arch/x86/machine/cpu.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 2360ea7..b384e55 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -596,13 +596,19 @@ void cpu_mp_setup(void); */ void cpu_ap_setup(void); +static inline unsigned int +cpu_apic_id(unsigned int cpu) +{ + return cpu_from_id(cpu)->apic_id; +} + /* * Send a cross-call interrupt to a remote processor. */ static inline void cpu_send_xcall(unsigned int cpu) { - lapic_ipi_send(cpu_from_id(cpu)->apic_id, TRAP_XCALL); + lapic_ipi_send(cpu_apic_id(cpu), TRAP_XCALL); } /* @@ -616,7 +622,7 @@ void cpu_xcall_intr(struct trap_frame *frame); static inline void cpu_send_thread_schedule(unsigned int cpu) { - lapic_ipi_send(cpu_from_id(cpu)->apic_id, TRAP_THREAD_SCHEDULE); + lapic_ipi_send(cpu_apic_id(cpu), TRAP_THREAD_SCHEDULE); } /* -- cgit v1.2.3 From b5451753e15bbe0a986ed32297f4a5de5c7e77ea Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 May 2017 20:32:07 +0200 Subject: kern/error: new ERROR_NODEV macro --- kern/error.c | 4 +++- kern/error.h | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kern/error.c b/kern/error.c index 043cdb9..e37c67c 100644 --- a/kern/error.c +++ b/kern/error.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Richard Braun. + * Copyright (c) 2014-2017 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 @@ -34,6 +34,8 @@ error_str(int error) return "device or resource busy"; case ERROR_FAULT: return "Bad address"; + case ERROR_NODEV: + return "No such device"; default: return "unknown error"; } diff --git a/kern/error.h b/kern/error.h index eb11cda..62859f4 100644 --- a/kern/error.h +++ b/kern/error.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -23,6 +23,7 @@ #define ERROR_INVAL 3 #define ERROR_BUSY 4 #define ERROR_FAULT 5 +#define ERROR_NODEV 6 /* * Return a string describing the given error. -- cgit v1.2.3 From d3742166baf0eca95875c61d5a72174b8e7bd0b2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 May 2017 20:32:30 +0200 Subject: x86/trap: rework to allow registering trap handlers --- arch/x86/machine/pic.c | 6 +- arch/x86/machine/trap.c | 145 ++++++------ arch/x86/machine/trap.h | 51 +++-- arch/x86/machine/trap_asm.S | 531 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 616 insertions(+), 117 deletions(-) diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index db26a16..412135c 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -57,8 +57,8 @@ pic_setup(void) io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); /* ICW 2 */ - io_write_byte(PIC_MASTER_IMR, TRAP_PIC_BASE); - io_write_byte(PIC_SLAVE_IMR, TRAP_PIC_BASE + PIC_NR_INTRS); + io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); + io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); /* ICW 3 - Set up cascading */ io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); @@ -96,7 +96,7 @@ pic_spurious_intr(struct trap_frame *frame) unsigned long intr; uint8_t isr; - intr = frame->vector - TRAP_PIC_BASE; + intr = frame->vector - TRAP_INTR_FIRST; assert((intr == PIC_SPURIOUS_INTR) || (intr == (PIC_NR_INTRS + PIC_SPURIOUS_INTR))); diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index aa045a9..3de4fd2 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -19,12 +19,15 @@ * additional configuration and resources to be properly handled. */ +#include #include #include +#include #include #include #include +#include #include #include #include @@ -43,7 +46,6 @@ static struct trap_cpu_data trap_cpu_data __percpu; * Type for interrupt service routines and trap handler functions. */ typedef void (*trap_isr_fn_t)(void); -typedef void (*trap_handler_fn_t)(struct trap_frame *); /* * Trap handler flags. @@ -59,42 +61,24 @@ struct trap_handler { }; /* - * Low level interrupt service routines. + * Table of ISR addresses. */ -void trap_isr_default(void); -void trap_isr_divide_error(void); -void trap_isr_debug(void); -void trap_isr_nmi(void); -void trap_isr_breakpoint(void); -void trap_isr_overflow(void); -void trap_isr_bound_range(void); -void trap_isr_invalid_opcode(void); -void trap_isr_device_not_available(void); -void trap_isr_double_fault(void); -void trap_isr_invalid_tss(void); -void trap_isr_segment_not_present(void); -void trap_isr_stack_segment_fault(void); -void trap_isr_general_protection(void); -void trap_isr_page_fault(void); -void trap_isr_math_fault(void); -void trap_isr_alignment_check(void); -void trap_isr_machine_check(void); -void trap_isr_simd_fp_exception(void); -void trap_isr_pic_int7(void); -void trap_isr_pic_int15(void); -void trap_isr_xcall(void); -void trap_isr_thread_schedule(void); -void trap_isr_cpu_halt(void); -void trap_isr_lapic_timer(void); -void trap_isr_lapic_error(void); -void trap_isr_lapic_spurious(void); +extern trap_isr_fn_t trap_isr_table[CPU_IDT_SIZE]; /* * Array of trap handlers. + */ +static struct trap_handler trap_handlers[CPU_IDT_SIZE] __read_mostly; + +/* + * Global trap lock. + * + * This lock is currently only used to serialize concurrent trap handler + * updates. * - * The additional entry is the default entry used for unhandled traps. + * Interrupts must be disabled when holding this lock. */ -static struct trap_handler trap_handlers[CPU_IDT_SIZE + 1] __read_mostly; +static struct spinlock trap_lock; static struct trap_handler * trap_handler_get(unsigned int vector) @@ -107,17 +91,14 @@ static void __init trap_handler_init(struct trap_handler *handler, int flags, trap_handler_fn_t fn) { handler->flags = flags; - handler->fn = fn; + atomic_store(&handler->fn, fn, ATOMIC_RELAXED); } static void __init -trap_install(unsigned int vector, int flags, trap_isr_fn_t isr, - trap_handler_fn_t fn) +trap_install(unsigned int vector, int flags, trap_handler_fn_t fn) { - assert(vector < CPU_IDT_SIZE); - + assert(vector < ARRAY_SIZE(trap_handlers)); trap_handler_init(trap_handler_get(vector), flags, fn); - cpu_idt_set_gate(vector, isr); } static void @@ -175,9 +156,8 @@ trap_double_fault(struct trap_frame *frame) static void __init trap_install_double_fault(void) { - trap_handler_init(trap_handler_get(TRAP_DF), - TRAP_HF_INTR, trap_double_fault); - cpu_idt_set_double_fault(trap_isr_double_fault); + trap_install(TRAP_DF, TRAP_HF_INTR, trap_double_fault); + cpu_idt_set_double_fault(trap_isr_table[TRAP_DF]); } static void @@ -196,58 +176,50 @@ trap_setup(void) { size_t i; - for (i = 0; i < CPU_IDT_SIZE; i++) { - trap_install(i, TRAP_HF_INTR, trap_isr_default, trap_default); + spinlock_init(&trap_lock); + + for (i = 0; i < ARRAY_SIZE(trap_isr_table); i++) { + cpu_idt_set_gate(i, trap_isr_table[i]); + } + + for (i = 0; i < ARRAY_SIZE(trap_handlers); i++) { + trap_install(i, TRAP_HF_INTR, trap_default); } /* Architecture defined traps */ - trap_install(TRAP_DE, 0, trap_isr_divide_error, trap_default); - trap_install(TRAP_DB, 0, trap_isr_debug, trap_default); - trap_install(TRAP_NMI, TRAP_HF_INTR, trap_isr_nmi, trap_default); - trap_install(TRAP_BP, 0, trap_isr_breakpoint, trap_default); - trap_install(TRAP_OF, 0, trap_isr_overflow, trap_default); - trap_install(TRAP_BR, 0, trap_isr_bound_range, trap_default); - trap_install(TRAP_UD, 0, trap_isr_invalid_opcode, trap_default); - trap_install(TRAP_NM, 0, trap_isr_device_not_available, trap_default); + trap_install(TRAP_DE, 0, trap_default); + trap_install(TRAP_DB, 0, trap_default); + trap_install(TRAP_NMI, TRAP_HF_INTR, trap_default); + trap_install(TRAP_BP, 0, trap_default); + trap_install(TRAP_OF, 0, trap_default); + trap_install(TRAP_BR, 0, trap_default); + trap_install(TRAP_UD, 0, trap_default); + trap_install(TRAP_NM, 0, trap_default); trap_install_double_fault(); - trap_install(TRAP_TS, 0, trap_isr_invalid_tss, trap_default); - trap_install(TRAP_NP, 0, trap_isr_segment_not_present, trap_default); - trap_install(TRAP_SS, 0, trap_isr_stack_segment_fault, trap_default); - trap_install(TRAP_GP, 0, trap_isr_general_protection, trap_default); - trap_install(TRAP_PF, 0, trap_isr_page_fault, trap_default); - trap_install(TRAP_MF, 0, trap_isr_math_fault, trap_default); - trap_install(TRAP_AC, 0, trap_isr_alignment_check, trap_default); - trap_install(TRAP_MC, TRAP_HF_INTR, trap_isr_machine_check, trap_default); - trap_install(TRAP_XM, 0, trap_isr_simd_fp_exception, trap_default); - - /* Basic PIC support */ - trap_install(TRAP_PIC_BASE + 7, TRAP_HF_INTR, - trap_isr_pic_int7, pic_spurious_intr); - trap_install(TRAP_PIC_BASE + 15, TRAP_HF_INTR, - trap_isr_pic_int15, pic_spurious_intr); + trap_install(TRAP_TS, 0, trap_default); + trap_install(TRAP_NP, 0, trap_default); + trap_install(TRAP_SS, 0, trap_default); + trap_install(TRAP_GP, 0, trap_default); + trap_install(TRAP_PF, 0, trap_default); + trap_install(TRAP_MF, 0, trap_default); + trap_install(TRAP_AC, 0, trap_default); + trap_install(TRAP_MC, TRAP_HF_INTR, trap_default); + trap_install(TRAP_XM, 0, trap_default); /* System defined traps */ - trap_install(TRAP_XCALL, TRAP_HF_INTR, - trap_isr_xcall, cpu_xcall_intr); - trap_install(TRAP_THREAD_SCHEDULE, TRAP_HF_INTR, - trap_isr_thread_schedule, cpu_thread_schedule_intr); - trap_install(TRAP_CPU_HALT, TRAP_HF_INTR, - trap_isr_cpu_halt, cpu_halt_intr); - trap_install(TRAP_LAPIC_TIMER, TRAP_HF_INTR, - trap_isr_lapic_timer, lapic_timer_intr); - trap_install(TRAP_LAPIC_ERROR, TRAP_HF_INTR, - trap_isr_lapic_error, lapic_error_intr); - trap_install(TRAP_LAPIC_SPURIOUS, TRAP_HF_INTR, - trap_isr_lapic_spurious, lapic_spurious_intr); - - trap_handler_init(trap_handler_get(TRAP_DEFAULT), - TRAP_HF_INTR, trap_default); + trap_install(TRAP_XCALL, TRAP_HF_INTR, cpu_xcall_intr); + trap_install(TRAP_THREAD_SCHEDULE, TRAP_HF_INTR, cpu_thread_schedule_intr); + trap_install(TRAP_CPU_HALT, TRAP_HF_INTR, cpu_halt_intr); + trap_install(TRAP_LAPIC_TIMER, TRAP_HF_INTR, lapic_timer_intr); + trap_install(TRAP_LAPIC_ERROR, TRAP_HF_INTR, lapic_error_intr); + trap_install(TRAP_LAPIC_SPURIOUS, TRAP_HF_INTR, lapic_spurious_intr); } void trap_main(struct trap_frame *frame) { struct trap_handler *handler; + trap_handler_fn_t fn; assert(!cpu_intr_enabled()); @@ -257,7 +229,8 @@ trap_main(struct trap_frame *frame) thread_intr_enter(); } - handler->fn(frame); + fn = atomic_load(&handler->fn, ATOMIC_RELAXED); + fn(frame); if (handler->flags & TRAP_HF_INTR) { thread_intr_leave(); @@ -266,6 +239,16 @@ trap_main(struct trap_frame *frame) assert(!cpu_intr_enabled()); } +void +trap_register(unsigned int vector, trap_handler_fn_t handler_fn) +{ + unsigned long flags; + + spinlock_lock_intr_save(&trap_lock, &flags); + trap_install(vector, TRAP_HF_INTR, handler_fn); + spinlock_unlock_intr_restore(&trap_lock, flags); +} + #ifdef __LP64__ void diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index 2f53f17..7bda9cf 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -24,29 +24,30 @@ /* * Architecture defined traps. */ -#define TRAP_DE 0 /* Divide Error */ -#define TRAP_DB 1 /* Debug */ -#define TRAP_NMI 2 /* NMI Interrupt */ -#define TRAP_BP 3 /* Breakpoint */ -#define TRAP_OF 4 /* Overflow */ -#define TRAP_BR 5 /* BOUND Range Exceeded */ -#define TRAP_UD 6 /* Invalid Opcode (Undefined Opcode) */ -#define TRAP_NM 7 /* Device Not Available (No Math Coprocessor) */ -#define TRAP_DF 8 /* Double Fault */ -#define TRAP_TS 10 /* Invalid TSS */ -#define TRAP_NP 11 /* Segment Not Present */ -#define TRAP_SS 12 /* Stack-Segment Fault */ -#define TRAP_GP 13 /* General Protection */ -#define TRAP_PF 14 /* Page Fault */ -#define TRAP_MF 16 /* x87 FPU Floating-Point Error (Math Fault) */ -#define TRAP_AC 17 /* Alignment Check */ -#define TRAP_MC 18 /* Machine Check */ -#define TRAP_XM 19 /* SIMD Floating-Point Exception */ +#define TRAP_DE 0 /* Divide Error */ +#define TRAP_DB 1 /* Debug */ +#define TRAP_NMI 2 /* NMI Interrupt */ +#define TRAP_BP 3 /* Breakpoint */ +#define TRAP_OF 4 /* Overflow */ +#define TRAP_BR 5 /* BOUND Range Exceeded */ +#define TRAP_UD 6 /* Invalid Opcode (Undefined Opcode) */ +#define TRAP_NM 7 /* Device Not Available (No Math Coprocessor) */ +#define TRAP_DF 8 /* Double Fault */ +#define TRAP_TS 10 /* Invalid TSS */ +#define TRAP_NP 11 /* Segment Not Present */ +#define TRAP_SS 12 /* Stack-Segment Fault */ +#define TRAP_GP 13 /* General Protection */ +#define TRAP_PF 14 /* Page Fault */ +#define TRAP_MF 16 /* x87 FPU Floating-Point Error (Math Fault) */ +#define TRAP_AC 17 /* Alignment Check */ +#define TRAP_MC 18 /* Machine Check */ +#define TRAP_XM 19 /* SIMD Floating-Point Exception */ /* - * Interrupts reserved for the legacy PIC. + * Traps used for handling external interrupts. */ -#define TRAP_PIC_BASE 32 +#define TRAP_INTR_FIRST 32 +#define TRAP_INTR_LAST 223 /* * System defined traps. @@ -124,6 +125,11 @@ struct trap_frame { #endif /* __LP64__ */ +/* + * Type for trap handler functions. + */ +typedef void (*trap_handler_fn_t)(struct trap_frame *); + static inline void trap_test_double_fault(void) { @@ -141,6 +147,11 @@ void trap_setup(void); */ void trap_main(struct trap_frame *frame); +/* + * Register a trap handler. + */ +void trap_register(unsigned int vector, trap_handler_fn_t handler_fn); + /* * Display the content of a trap frame. */ diff --git a/arch/x86/machine/trap_asm.S b/arch/x86/machine/trap_asm.S index 15e8c78..a60bbf0 100644 --- a/arch/x86/machine/trap_asm.S +++ b/arch/x86/machine/trap_asm.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -15,7 +15,9 @@ * along with this program. If not, see . */ +#include #include +#include #include .text @@ -172,17 +174,520 @@ TRAP_ERROR(TRAP_AC, trap_isr_alignment_check) TRAP(TRAP_MC, trap_isr_machine_check) TRAP(TRAP_XM, trap_isr_simd_fp_exception) -/* Basic PIC support */ -TRAP(TRAP_PIC_BASE + 7, trap_isr_pic_int7) -TRAP(TRAP_PIC_BASE + 15, trap_isr_pic_int15) +/* + * Handlers for reserved exceptions. + * + * These exceptions should normally never occur, but have handlers ready just + * in case. + */ +TRAP(9, trap_isr_9) +TRAP(15, trap_isr_15) +TRAP(20, trap_isr_20) +TRAP(21, trap_isr_21) +TRAP(22, trap_isr_22) +TRAP(23, trap_isr_23) +TRAP(24, trap_isr_24) +TRAP(25, trap_isr_25) +TRAP(26, trap_isr_26) +TRAP(27, trap_isr_27) +TRAP(28, trap_isr_28) +TRAP(29, trap_isr_29) +TRAP(31, trap_isr_31) + +/* Generic trap handlers */ +TRAP(30, trap_isr_30) +TRAP(32, trap_isr_32) +TRAP(33, trap_isr_33) +TRAP(34, trap_isr_34) +TRAP(35, trap_isr_35) +TRAP(36, trap_isr_36) +TRAP(37, trap_isr_37) +TRAP(38, trap_isr_38) +TRAP(39, trap_isr_39) +TRAP(40, trap_isr_40) +TRAP(41, trap_isr_41) +TRAP(42, trap_isr_42) +TRAP(43, trap_isr_43) +TRAP(44, trap_isr_44) +TRAP(45, trap_isr_45) +TRAP(46, trap_isr_46) +TRAP(47, trap_isr_47) +TRAP(48, trap_isr_48) +TRAP(49, trap_isr_49) +TRAP(50, trap_isr_50) +TRAP(51, trap_isr_51) +TRAP(52, trap_isr_52) +TRAP(53, trap_isr_53) +TRAP(54, trap_isr_54) +TRAP(55, trap_isr_55) +TRAP(56, trap_isr_56) +TRAP(57, trap_isr_57) +TRAP(58, trap_isr_58) +TRAP(59, trap_isr_59) +TRAP(60, trap_isr_60) +TRAP(61, trap_isr_61) +TRAP(62, trap_isr_62) +TRAP(63, trap_isr_63) +TRAP(64, trap_isr_64) +TRAP(65, trap_isr_65) +TRAP(66, trap_isr_66) +TRAP(67, trap_isr_67) +TRAP(68, trap_isr_68) +TRAP(69, trap_isr_69) +TRAP(70, trap_isr_70) +TRAP(71, trap_isr_71) +TRAP(72, trap_isr_72) +TRAP(73, trap_isr_73) +TRAP(74, trap_isr_74) +TRAP(75, trap_isr_75) +TRAP(76, trap_isr_76) +TRAP(77, trap_isr_77) +TRAP(78, trap_isr_78) +TRAP(79, trap_isr_79) +TRAP(80, trap_isr_80) +TRAP(81, trap_isr_81) +TRAP(82, trap_isr_82) +TRAP(83, trap_isr_83) +TRAP(84, trap_isr_84) +TRAP(85, trap_isr_85) +TRAP(86, trap_isr_86) +TRAP(87, trap_isr_87) +TRAP(88, trap_isr_88) +TRAP(89, trap_isr_89) +TRAP(90, trap_isr_90) +TRAP(91, trap_isr_91) +TRAP(92, trap_isr_92) +TRAP(93, trap_isr_93) +TRAP(94, trap_isr_94) +TRAP(95, trap_isr_95) +TRAP(96, trap_isr_96) +TRAP(97, trap_isr_97) +TRAP(98, trap_isr_98) +TRAP(99, trap_isr_99) +TRAP(100, trap_isr_100) +TRAP(101, trap_isr_101) +TRAP(102, trap_isr_102) +TRAP(103, trap_isr_103) +TRAP(104, trap_isr_104) +TRAP(105, trap_isr_105) +TRAP(106, trap_isr_106) +TRAP(107, trap_isr_107) +TRAP(108, trap_isr_108) +TRAP(109, trap_isr_109) +TRAP(110, trap_isr_110) +TRAP(111, trap_isr_111) +TRAP(112, trap_isr_112) +TRAP(113, trap_isr_113) +TRAP(114, trap_isr_114) +TRAP(115, trap_isr_115) +TRAP(116, trap_isr_116) +TRAP(117, trap_isr_117) +TRAP(118, trap_isr_118) +TRAP(119, trap_isr_119) +TRAP(120, trap_isr_120) +TRAP(121, trap_isr_121) +TRAP(122, trap_isr_122) +TRAP(123, trap_isr_123) +TRAP(124, trap_isr_124) +TRAP(125, trap_isr_125) +TRAP(126, trap_isr_126) +TRAP(127, trap_isr_127) +TRAP(128, trap_isr_128) +TRAP(129, trap_isr_129) +TRAP(130, trap_isr_130) +TRAP(131, trap_isr_131) +TRAP(132, trap_isr_132) +TRAP(133, trap_isr_133) +TRAP(134, trap_isr_134) +TRAP(135, trap_isr_135) +TRAP(136, trap_isr_136) +TRAP(137, trap_isr_137) +TRAP(138, trap_isr_138) +TRAP(139, trap_isr_139) +TRAP(140, trap_isr_140) +TRAP(141, trap_isr_141) +TRAP(142, trap_isr_142) +TRAP(143, trap_isr_143) +TRAP(144, trap_isr_144) +TRAP(145, trap_isr_145) +TRAP(146, trap_isr_146) +TRAP(147, trap_isr_147) +TRAP(148, trap_isr_148) +TRAP(149, trap_isr_149) +TRAP(150, trap_isr_150) +TRAP(151, trap_isr_151) +TRAP(152, trap_isr_152) +TRAP(153, trap_isr_153) +TRAP(154, trap_isr_154) +TRAP(155, trap_isr_155) +TRAP(156, trap_isr_156) +TRAP(157, trap_isr_157) +TRAP(158, trap_isr_158) +TRAP(159, trap_isr_159) +TRAP(160, trap_isr_160) +TRAP(161, trap_isr_161) +TRAP(162, trap_isr_162) +TRAP(163, trap_isr_163) +TRAP(164, trap_isr_164) +TRAP(165, trap_isr_165) +TRAP(166, trap_isr_166) +TRAP(167, trap_isr_167) +TRAP(168, trap_isr_168) +TRAP(169, trap_isr_169) +TRAP(170, trap_isr_170) +TRAP(171, trap_isr_171) +TRAP(172, trap_isr_172) +TRAP(173, trap_isr_173) +TRAP(174, trap_isr_174) +TRAP(175, trap_isr_175) +TRAP(176, trap_isr_176) +TRAP(177, trap_isr_177) +TRAP(178, trap_isr_178) +TRAP(179, trap_isr_179) +TRAP(180, trap_isr_180) +TRAP(181, trap_isr_181) +TRAP(182, trap_isr_182) +TRAP(183, trap_isr_183) +TRAP(184, trap_isr_184) +TRAP(185, trap_isr_185) +TRAP(186, trap_isr_186) +TRAP(187, trap_isr_187) +TRAP(188, trap_isr_188) +TRAP(189, trap_isr_189) +TRAP(190, trap_isr_190) +TRAP(191, trap_isr_191) +TRAP(192, trap_isr_192) +TRAP(193, trap_isr_193) +TRAP(194, trap_isr_194) +TRAP(195, trap_isr_195) +TRAP(196, trap_isr_196) +TRAP(197, trap_isr_197) +TRAP(198, trap_isr_198) +TRAP(199, trap_isr_199) +TRAP(200, trap_isr_200) +TRAP(201, trap_isr_201) +TRAP(202, trap_isr_202) +TRAP(203, trap_isr_203) +TRAP(204, trap_isr_204) +TRAP(205, trap_isr_205) +TRAP(206, trap_isr_206) +TRAP(207, trap_isr_207) +TRAP(208, trap_isr_208) +TRAP(209, trap_isr_209) +TRAP(210, trap_isr_210) +TRAP(211, trap_isr_211) +TRAP(212, trap_isr_212) +TRAP(213, trap_isr_213) +TRAP(214, trap_isr_214) +TRAP(215, trap_isr_215) +TRAP(216, trap_isr_216) +TRAP(217, trap_isr_217) +TRAP(218, trap_isr_218) +TRAP(219, trap_isr_219) +TRAP(220, trap_isr_220) +TRAP(221, trap_isr_221) +TRAP(222, trap_isr_222) +TRAP(223, trap_isr_223) +TRAP(224, trap_isr_224) +TRAP(225, trap_isr_225) +TRAP(226, trap_isr_226) +TRAP(227, trap_isr_227) +TRAP(228, trap_isr_228) +TRAP(229, trap_isr_229) +TRAP(230, trap_isr_230) +TRAP(231, trap_isr_231) +TRAP(232, trap_isr_232) +TRAP(233, trap_isr_233) +TRAP(234, trap_isr_234) +TRAP(235, trap_isr_235) +TRAP(236, trap_isr_236) +TRAP(237, trap_isr_237) +TRAP(238, trap_isr_238) +TRAP(239, trap_isr_239) +TRAP(240, trap_isr_240) +TRAP(241, trap_isr_241) +TRAP(242, trap_isr_242) +TRAP(243, trap_isr_243) +TRAP(244, trap_isr_244) +TRAP(245, trap_isr_245) +TRAP(246, trap_isr_246) +TRAP(247, trap_isr_247) +TRAP(248, trap_isr_248) +TRAP(249, trap_isr_249) +TRAP(250, trap_isr_250) +TRAP(251, trap_isr_251) +TRAP(252, trap_isr_252) +TRAP(253, trap_isr_253) +TRAP(254, trap_isr_254) +TRAP(255, trap_isr_255) + +#ifdef __LP64__ +#define TRAP_TABLE_ENTRY(name) .quad name +#else /* __LP64__ */ +#define TRAP_TABLE_ENTRY(name) .long name +#endif /* __LP64__ */ -/* System defined traps */ -TRAP(TRAP_XCALL, trap_isr_xcall) -TRAP(TRAP_THREAD_SCHEDULE, trap_isr_thread_schedule) -TRAP(TRAP_CPU_HALT, trap_isr_cpu_halt) -TRAP(TRAP_LAPIC_TIMER, trap_isr_lapic_timer) -TRAP(TRAP_LAPIC_ERROR, trap_isr_lapic_error) -TRAP(TRAP_LAPIC_SPURIOUS, trap_isr_lapic_spurious) +.section INIT_DATA_SECTION -/* Unhandled traps */ -TRAP(TRAP_DEFAULT, trap_isr_default) +/* + * This table lists all interrupt service routines as installed in the + * IDT. + */ +ASM_DATA(trap_isr_table) +TRAP_TABLE_ENTRY(trap_isr_divide_error) +TRAP_TABLE_ENTRY(trap_isr_debug) +TRAP_TABLE_ENTRY(trap_isr_nmi) +TRAP_TABLE_ENTRY(trap_isr_breakpoint) +TRAP_TABLE_ENTRY(trap_isr_overflow) +TRAP_TABLE_ENTRY(trap_isr_bound_range) +TRAP_TABLE_ENTRY(trap_isr_invalid_opcode) +TRAP_TABLE_ENTRY(trap_isr_device_not_available) +TRAP_TABLE_ENTRY(trap_isr_double_fault) +TRAP_TABLE_ENTRY(trap_isr_9) +TRAP_TABLE_ENTRY(trap_isr_invalid_tss) +TRAP_TABLE_ENTRY(trap_isr_segment_not_present) +TRAP_TABLE_ENTRY(trap_isr_stack_segment_fault) +TRAP_TABLE_ENTRY(trap_isr_general_protection) +TRAP_TABLE_ENTRY(trap_isr_page_fault) +TRAP_TABLE_ENTRY(trap_isr_15) +TRAP_TABLE_ENTRY(trap_isr_math_fault) +TRAP_TABLE_ENTRY(trap_isr_alignment_check) +TRAP_TABLE_ENTRY(trap_isr_machine_check) +TRAP_TABLE_ENTRY(trap_isr_simd_fp_exception) +TRAP_TABLE_ENTRY(trap_isr_20) +TRAP_TABLE_ENTRY(trap_isr_21) +TRAP_TABLE_ENTRY(trap_isr_22) +TRAP_TABLE_ENTRY(trap_isr_23) +TRAP_TABLE_ENTRY(trap_isr_24) +TRAP_TABLE_ENTRY(trap_isr_25) +TRAP_TABLE_ENTRY(trap_isr_26) +TRAP_TABLE_ENTRY(trap_isr_27) +TRAP_TABLE_ENTRY(trap_isr_28) +TRAP_TABLE_ENTRY(trap_isr_29) +TRAP_TABLE_ENTRY(trap_isr_30) +TRAP_TABLE_ENTRY(trap_isr_31) +TRAP_TABLE_ENTRY(trap_isr_32) +TRAP_TABLE_ENTRY(trap_isr_33) +TRAP_TABLE_ENTRY(trap_isr_34) +TRAP_TABLE_ENTRY(trap_isr_35) +TRAP_TABLE_ENTRY(trap_isr_36) +TRAP_TABLE_ENTRY(trap_isr_37) +TRAP_TABLE_ENTRY(trap_isr_38) +TRAP_TABLE_ENTRY(trap_isr_39) +TRAP_TABLE_ENTRY(trap_isr_40) +TRAP_TABLE_ENTRY(trap_isr_41) +TRAP_TABLE_ENTRY(trap_isr_42) +TRAP_TABLE_ENTRY(trap_isr_43) +TRAP_TABLE_ENTRY(trap_isr_44) +TRAP_TABLE_ENTRY(trap_isr_45) +TRAP_TABLE_ENTRY(trap_isr_46) +TRAP_TABLE_ENTRY(trap_isr_47) +TRAP_TABLE_ENTRY(trap_isr_48) +TRAP_TABLE_ENTRY(trap_isr_49) +TRAP_TABLE_ENTRY(trap_isr_50) +TRAP_TABLE_ENTRY(trap_isr_51) +TRAP_TABLE_ENTRY(trap_isr_52) +TRAP_TABLE_ENTRY(trap_isr_53) +TRAP_TABLE_ENTRY(trap_isr_54) +TRAP_TABLE_ENTRY(trap_isr_55) +TRAP_TABLE_ENTRY(trap_isr_56) +TRAP_TABLE_ENTRY(trap_isr_57) +TRAP_TABLE_ENTRY(trap_isr_58) +TRAP_TABLE_ENTRY(trap_isr_59) +TRAP_TABLE_ENTRY(trap_isr_60) +TRAP_TABLE_ENTRY(trap_isr_61) +TRAP_TABLE_ENTRY(trap_isr_62) +TRAP_TABLE_ENTRY(trap_isr_63) +TRAP_TABLE_ENTRY(trap_isr_64) +TRAP_TABLE_ENTRY(trap_isr_65) +TRAP_TABLE_ENTRY(trap_isr_66) +TRAP_TABLE_ENTRY(trap_isr_67) +TRAP_TABLE_ENTRY(trap_isr_68) +TRAP_TABLE_ENTRY(trap_isr_69) +TRAP_TABLE_ENTRY(trap_isr_70) +TRAP_TABLE_ENTRY(trap_isr_71) +TRAP_TABLE_ENTRY(trap_isr_72) +TRAP_TABLE_ENTRY(trap_isr_73) +TRAP_TABLE_ENTRY(trap_isr_74) +TRAP_TABLE_ENTRY(trap_isr_75) +TRAP_TABLE_ENTRY(trap_isr_76) +TRAP_TABLE_ENTRY(trap_isr_77) +TRAP_TABLE_ENTRY(trap_isr_78) +TRAP_TABLE_ENTRY(trap_isr_79) +TRAP_TABLE_ENTRY(trap_isr_80) +TRAP_TABLE_ENTRY(trap_isr_81) +TRAP_TABLE_ENTRY(trap_isr_82) +TRAP_TABLE_ENTRY(trap_isr_83) +TRAP_TABLE_ENTRY(trap_isr_84) +TRAP_TABLE_ENTRY(trap_isr_85) +TRAP_TABLE_ENTRY(trap_isr_86) +TRAP_TABLE_ENTRY(trap_isr_87) +TRAP_TABLE_ENTRY(trap_isr_88) +TRAP_TABLE_ENTRY(trap_isr_89) +TRAP_TABLE_ENTRY(trap_isr_90) +TRAP_TABLE_ENTRY(trap_isr_91) +TRAP_TABLE_ENTRY(trap_isr_92) +TRAP_TABLE_ENTRY(trap_isr_93) +TRAP_TABLE_ENTRY(trap_isr_94) +TRAP_TABLE_ENTRY(trap_isr_95) +TRAP_TABLE_ENTRY(trap_isr_96) +TRAP_TABLE_ENTRY(trap_isr_97) +TRAP_TABLE_ENTRY(trap_isr_98) +TRAP_TABLE_ENTRY(trap_isr_99) +TRAP_TABLE_ENTRY(trap_isr_100) +TRAP_TABLE_ENTRY(trap_isr_101) +TRAP_TABLE_ENTRY(trap_isr_102) +TRAP_TABLE_ENTRY(trap_isr_103) +TRAP_TABLE_ENTRY(trap_isr_104) +TRAP_TABLE_ENTRY(trap_isr_105) +TRAP_TABLE_ENTRY(trap_isr_106) +TRAP_TABLE_ENTRY(trap_isr_107) +TRAP_TABLE_ENTRY(trap_isr_108) +TRAP_TABLE_ENTRY(trap_isr_109) +TRAP_TABLE_ENTRY(trap_isr_110) +TRAP_TABLE_ENTRY(trap_isr_111) +TRAP_TABLE_ENTRY(trap_isr_112) +TRAP_TABLE_ENTRY(trap_isr_113) +TRAP_TABLE_ENTRY(trap_isr_114) +TRAP_TABLE_ENTRY(trap_isr_115) +TRAP_TABLE_ENTRY(trap_isr_116) +TRAP_TABLE_ENTRY(trap_isr_117) +TRAP_TABLE_ENTRY(trap_isr_118) +TRAP_TABLE_ENTRY(trap_isr_119) +TRAP_TABLE_ENTRY(trap_isr_120) +TRAP_TABLE_ENTRY(trap_isr_121) +TRAP_TABLE_ENTRY(trap_isr_122) +TRAP_TABLE_ENTRY(trap_isr_123) +TRAP_TABLE_ENTRY(trap_isr_124) +TRAP_TABLE_ENTRY(trap_isr_125) +TRAP_TABLE_ENTRY(trap_isr_126) +TRAP_TABLE_ENTRY(trap_isr_127) +TRAP_TABLE_ENTRY(trap_isr_128) +TRAP_TABLE_ENTRY(trap_isr_129) +TRAP_TABLE_ENTRY(trap_isr_130) +TRAP_TABLE_ENTRY(trap_isr_131) +TRAP_TABLE_ENTRY(trap_isr_132) +TRAP_TABLE_ENTRY(trap_isr_133) +TRAP_TABLE_ENTRY(trap_isr_134) +TRAP_TABLE_ENTRY(trap_isr_135) +TRAP_TABLE_ENTRY(trap_isr_136) +TRAP_TABLE_ENTRY(trap_isr_137) +TRAP_TABLE_ENTRY(trap_isr_138) +TRAP_TABLE_ENTRY(trap_isr_139) +TRAP_TABLE_ENTRY(trap_isr_140) +TRAP_TABLE_ENTRY(trap_isr_141) +TRAP_TABLE_ENTRY(trap_isr_142) +TRAP_TABLE_ENTRY(trap_isr_143) +TRAP_TABLE_ENTRY(trap_isr_144) +TRAP_TABLE_ENTRY(trap_isr_145) +TRAP_TABLE_ENTRY(trap_isr_146) +TRAP_TABLE_ENTRY(trap_isr_147) +TRAP_TABLE_ENTRY(trap_isr_148) +TRAP_TABLE_ENTRY(trap_isr_149) +TRAP_TABLE_ENTRY(trap_isr_150) +TRAP_TABLE_ENTRY(trap_isr_151) +TRAP_TABLE_ENTRY(trap_isr_152) +TRAP_TABLE_ENTRY(trap_isr_153) +TRAP_TABLE_ENTRY(trap_isr_154) +TRAP_TABLE_ENTRY(trap_isr_155) +TRAP_TABLE_ENTRY(trap_isr_156) +TRAP_TABLE_ENTRY(trap_isr_157) +TRAP_TABLE_ENTRY(trap_isr_158) +TRAP_TABLE_ENTRY(trap_isr_159) +TRAP_TABLE_ENTRY(trap_isr_160) +TRAP_TABLE_ENTRY(trap_isr_161) +TRAP_TABLE_ENTRY(trap_isr_162) +TRAP_TABLE_ENTRY(trap_isr_163) +TRAP_TABLE_ENTRY(trap_isr_164) +TRAP_TABLE_ENTRY(trap_isr_165) +TRAP_TABLE_ENTRY(trap_isr_166) +TRAP_TABLE_ENTRY(trap_isr_167) +TRAP_TABLE_ENTRY(trap_isr_168) +TRAP_TABLE_ENTRY(trap_isr_169) +TRAP_TABLE_ENTRY(trap_isr_170) +TRAP_TABLE_ENTRY(trap_isr_171) +TRAP_TABLE_ENTRY(trap_isr_172) +TRAP_TABLE_ENTRY(trap_isr_173) +TRAP_TABLE_ENTRY(trap_isr_174) +TRAP_TABLE_ENTRY(trap_isr_175) +TRAP_TABLE_ENTRY(trap_isr_176) +TRAP_TABLE_ENTRY(trap_isr_177) +TRAP_TABLE_ENTRY(trap_isr_178) +TRAP_TABLE_ENTRY(trap_isr_179) +TRAP_TABLE_ENTRY(trap_isr_180) +TRAP_TABLE_ENTRY(trap_isr_181) +TRAP_TABLE_ENTRY(trap_isr_182) +TRAP_TABLE_ENTRY(trap_isr_183) +TRAP_TABLE_ENTRY(trap_isr_184) +TRAP_TABLE_ENTRY(trap_isr_185) +TRAP_TABLE_ENTRY(trap_isr_186) +TRAP_TABLE_ENTRY(trap_isr_187) +TRAP_TABLE_ENTRY(trap_isr_188) +TRAP_TABLE_ENTRY(trap_isr_189) +TRAP_TABLE_ENTRY(trap_isr_190) +TRAP_TABLE_ENTRY(trap_isr_191) +TRAP_TABLE_ENTRY(trap_isr_192) +TRAP_TABLE_ENTRY(trap_isr_193) +TRAP_TABLE_ENTRY(trap_isr_194) +TRAP_TABLE_ENTRY(trap_isr_195) +TRAP_TABLE_ENTRY(trap_isr_196) +TRAP_TABLE_ENTRY(trap_isr_197) +TRAP_TABLE_ENTRY(trap_isr_198) +TRAP_TABLE_ENTRY(trap_isr_199) +TRAP_TABLE_ENTRY(trap_isr_200) +TRAP_TABLE_ENTRY(trap_isr_201) +TRAP_TABLE_ENTRY(trap_isr_202) +TRAP_TABLE_ENTRY(trap_isr_203) +TRAP_TABLE_ENTRY(trap_isr_204) +TRAP_TABLE_ENTRY(trap_isr_205) +TRAP_TABLE_ENTRY(trap_isr_206) +TRAP_TABLE_ENTRY(trap_isr_207) +TRAP_TABLE_ENTRY(trap_isr_208) +TRAP_TABLE_ENTRY(trap_isr_209) +TRAP_TABLE_ENTRY(trap_isr_210) +TRAP_TABLE_ENTRY(trap_isr_211) +TRAP_TABLE_ENTRY(trap_isr_212) +TRAP_TABLE_ENTRY(trap_isr_213) +TRAP_TABLE_ENTRY(trap_isr_214) +TRAP_TABLE_ENTRY(trap_isr_215) +TRAP_TABLE_ENTRY(trap_isr_216) +TRAP_TABLE_ENTRY(trap_isr_217) +TRAP_TABLE_ENTRY(trap_isr_218) +TRAP_TABLE_ENTRY(trap_isr_219) +TRAP_TABLE_ENTRY(trap_isr_220) +TRAP_TABLE_ENTRY(trap_isr_221) +TRAP_TABLE_ENTRY(trap_isr_222) +TRAP_TABLE_ENTRY(trap_isr_223) +TRAP_TABLE_ENTRY(trap_isr_224) +TRAP_TABLE_ENTRY(trap_isr_225) +TRAP_TABLE_ENTRY(trap_isr_226) +TRAP_TABLE_ENTRY(trap_isr_227) +TRAP_TABLE_ENTRY(trap_isr_228) +TRAP_TABLE_ENTRY(trap_isr_229) +TRAP_TABLE_ENTRY(trap_isr_230) +TRAP_TABLE_ENTRY(trap_isr_231) +TRAP_TABLE_ENTRY(trap_isr_232) +TRAP_TABLE_ENTRY(trap_isr_233) +TRAP_TABLE_ENTRY(trap_isr_234) +TRAP_TABLE_ENTRY(trap_isr_235) +TRAP_TABLE_ENTRY(trap_isr_236) +TRAP_TABLE_ENTRY(trap_isr_237) +TRAP_TABLE_ENTRY(trap_isr_238) +TRAP_TABLE_ENTRY(trap_isr_239) +TRAP_TABLE_ENTRY(trap_isr_240) +TRAP_TABLE_ENTRY(trap_isr_241) +TRAP_TABLE_ENTRY(trap_isr_242) +TRAP_TABLE_ENTRY(trap_isr_243) +TRAP_TABLE_ENTRY(trap_isr_244) +TRAP_TABLE_ENTRY(trap_isr_245) +TRAP_TABLE_ENTRY(trap_isr_246) +TRAP_TABLE_ENTRY(trap_isr_247) +TRAP_TABLE_ENTRY(trap_isr_248) +TRAP_TABLE_ENTRY(trap_isr_249) +TRAP_TABLE_ENTRY(trap_isr_250) +TRAP_TABLE_ENTRY(trap_isr_251) +TRAP_TABLE_ENTRY(trap_isr_252) +TRAP_TABLE_ENTRY(trap_isr_253) +TRAP_TABLE_ENTRY(trap_isr_254) +TRAP_TABLE_ENTRY(trap_isr_255) +ASM_END(trap_isr_table) -- cgit v1.2.3 From 9b1787cd1c821aa58a88795d68b65d4f5dc7dad0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 23 May 2017 20:34:58 +0200 Subject: x86/ioapic: new module --- arch/x86/Makefrag.am | 2 + arch/x86/machine/acpimp.c | 27 ++++- arch/x86/machine/ioapic.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/ioapic.h | 41 +++++++ 4 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 arch/x86/machine/ioapic.c create mode 100644 arch/x86/machine/ioapic.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index d5f1583..6adc769 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -41,6 +41,8 @@ x15_SOURCES += \ arch/x86/machine/cpu.h \ arch/x86/machine/elf.h \ arch/x86/machine/io.h \ + arch/x86/machine/ioapic.c \ + arch/x86/machine/ioapic.h \ arch/x86/machine/lapic.c \ arch/x86/machine/lapic.h \ arch/x86/machine/multiboot.h \ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 8d2433a..4e98683 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,8 @@ struct acpimp_rsdt { /* * MADT entry type codes. */ -#define ACPIMP_MADT_ENTRY_LAPIC 0 +#define ACPIMP_MADT_ENTRY_LAPIC 0 +#define ACPIMP_MADT_ENTRY_IOAPIC 1 struct acpimp_madt_entry_hdr { uint8_t type; @@ -92,10 +94,19 @@ struct acpimp_madt_entry_lapic { uint32_t flags; } __packed; +struct acpimp_madt_entry_ioapic { + struct acpimp_madt_entry_hdr header; + uint8_t id; + uint8_t _reserved; + uint32_t addr; + uint32_t base; +} __packed; + union acpimp_madt_entry { uint8_t type; struct acpimp_madt_entry_hdr header; struct acpimp_madt_entry_lapic lapic; + struct acpimp_madt_entry_ioapic ioapic; } __packed; struct acpimp_madt { @@ -466,6 +477,12 @@ acpimp_load_lapic(const struct acpimp_madt_entry_lapic *lapic, int *is_bsp) *is_bsp = 0; } +static void __init +acpimp_load_ioapic(const struct acpimp_madt_entry_ioapic *ioapic) +{ + ioapic_register(ioapic->id, ioapic->addr, ioapic->base); +} + static void __init acpimp_load_madt(void) { @@ -478,13 +495,21 @@ acpimp_load_madt(void) assert(table != NULL); madt = structof(table, struct acpimp_madt, header); lapic_setup(madt->lapic_addr); + ioapic_setup(); is_bsp = 1; + /* + * TODO Handle PCAT_COMPAT flag + * TODO Handle interrupt overrides + */ + acpimp_madt_foreach(madt, &iter) { switch (iter.entry->type) { case ACPIMP_MADT_ENTRY_LAPIC: acpimp_load_lapic(&iter.entry->lapic, &is_bsp); break; + case ACPIMP_MADT_ENTRY_IOAPIC: + acpimp_load_ioapic(&iter.entry->ioapic); } } } diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c new file mode 100644 index 0000000..48faf8d --- /dev/null +++ b/arch/x86/machine/ioapic.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2017 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 3 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 +#include + +#define IOAPIC_REG_VERSION 0x01 +#define IOAPIC_REG_IOREDTBL 0x10 + +#define IOAPIC_VERSION_VERSION_MASK 0x000000ff +#define IOAPIC_VERSION_VERSION_SHIFT 0 +#define IOAPIC_VERSION_MAXREDIR_MASK 0x00ff0000 +#define IOAPIC_VERSION_MAXREDIR_SHIFT 16 + +#define IOAPIC_ENTLOW_INTRMASK 0x10000 + +#define IOAPIC_MAX_ENTRIES 24 + +struct ioapic_map { + uint8_t regsel; + uint8_t _reserved0; + uint8_t _reserved1; + uint8_t _reserved2; + uint32_t _reserved3; + uint32_t _reserved4; + uint32_t _reserved5; + uint32_t win; +}; + +struct ioapic { + unsigned int id; + unsigned int version; + volatile struct ioapic_map *map; + unsigned int first_intr; + unsigned int last_intr; +}; + +static struct ioapic *ioapic_devs; +static unsigned int ioapic_nr_devs; + +/* + * Global lock. + * + * Interrupts must be disabled when holding this lock. + */ +static struct spinlock ioapic_lock; + +static struct ioapic * +ioapic_get(unsigned int id) +{ + assert(id < ioapic_nr_devs); + return &ioapic_devs[id]; +} + +static uint32_t +ioapic_read(struct ioapic *ioapic, uint8_t reg) +{ + ioapic->map->regsel = reg; + return ioapic->map->win; +} + +static void +ioapic_write(struct ioapic *ioapic, uint8_t reg, uint32_t value) +{ + ioapic->map->regsel = reg; + ioapic->map->win = value; +} + +static void +ioapic_write_entry_low(struct ioapic *ioapic, unsigned int id, uint32_t value) +{ + assert(id < IOAPIC_MAX_ENTRIES); + ioapic_write(ioapic, IOAPIC_REG_IOREDTBL + (id * 2), value); +} + +static void +ioapic_write_entry_high(struct ioapic *ioapic, unsigned int id, uint32_t value) +{ + assert(id < IOAPIC_MAX_ENTRIES); + ioapic_write(ioapic, IOAPIC_REG_IOREDTBL + (id * 2) + 1, value); +} + +static bool +ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) +{ + return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); +} + +static unsigned int +ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) +{ + assert(ioapic_has_intr(ioapic, intr)); + return intr - ioapic->first_intr; +} + +static void +ioapic_enable_intr(struct ioapic *ioapic, unsigned int intr, + unsigned int cpu, unsigned int vector) +{ + unsigned int id; + + id = ioapic_compute_id(ioapic, intr); + ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); + ioapic_write_entry_low(ioapic, id, vector); +} + +static void +ioapic_disable_intr(struct ioapic *ioapic, unsigned int intr) +{ + unsigned int id; + + id = ioapic_compute_id(ioapic, intr); + ioapic_write_entry_low(ioapic, id, IOAPIC_ENTLOW_INTRMASK); +} + +static void +ioapic_intr(struct trap_frame *frame) +{ + lapic_eoi(); + printf("ioapic: cpu:%u vector:%lu\n", cpu_id(), frame->vector); +} + +void __init +ioapic_setup(void) +{ + ioapic_devs = NULL; + ioapic_nr_devs = 0; + spinlock_init(&ioapic_lock); +} + +static void __init +ioapic_init(struct ioapic *ioapic, unsigned int id, + uintptr_t addr, unsigned int intr_base) +{ + unsigned int i, nr_intrs; + uint32_t value; + + ioapic->id = id; + ioapic->first_intr = intr_base; + + ioapic->map = vm_kmem_map_pa(addr, sizeof(*ioapic->map), NULL, NULL); + + if (ioapic->map == NULL) { + panic("ioapic: unable to map register window in kernel map"); + } + + value = ioapic_read(ioapic, IOAPIC_REG_VERSION); + ioapic->version = (value & IOAPIC_VERSION_VERSION_MASK) + >> IOAPIC_VERSION_VERSION_SHIFT; + nr_intrs = ((value & IOAPIC_VERSION_MAXREDIR_MASK) + >> IOAPIC_VERSION_MAXREDIR_SHIFT) + 1; + ioapic->last_intr = ioapic->first_intr + nr_intrs - 1; + + if (ioapic->last_intr > (TRAP_INTR_LAST - TRAP_INTR_FIRST)) { + panic("ioapic: invalid interrupt range"); + } + + printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, + ioapic->version, ioapic->first_intr, ioapic->last_intr); + + for (i = ioapic->first_intr; i < ioapic->last_intr; i++) { + trap_register(TRAP_INTR_FIRST + i, ioapic_intr); + } +} + +void __init +ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) +{ + struct ioapic *tmp; + + spinlock_lock(&ioapic_lock); + + tmp = kmem_alloc((ioapic_nr_devs + 1) * sizeof(*ioapic_devs)); + + if (tmp == NULL) { + panic("ioapic: unable to allocate memory for device"); + } + + memcpy(tmp, ioapic_devs, ioapic_nr_devs); + kmem_free(ioapic_devs, ioapic_nr_devs * sizeof(*ioapic_devs)); + ioapic_devs = tmp; + ioapic_nr_devs++; + + ioapic_init(ioapic_get(ioapic_nr_devs - 1), id, addr, intr_base); + + spinlock_unlock(&ioapic_lock); +} + +static struct ioapic * +ioapic_lookup(unsigned int intr) +{ + struct ioapic *ioapic; + unsigned int i; + + for (i = 0; i < ioapic_nr_devs; i++) { + ioapic = &ioapic_devs[i]; + + if ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)) { + return ioapic; + } + } + + return NULL; +} + +int +ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector) +{ + struct ioapic *ioapic; + unsigned long flags; + int error; + + spinlock_lock_intr_save(&ioapic_lock, &flags); + + ioapic = ioapic_lookup(intr); + + if (ioapic == NULL) { + error = ERROR_NODEV; + goto out; + } + + ioapic_enable_intr(ioapic, intr, cpu, vector); + error = 0; + +out: + spinlock_unlock_intr_restore(&ioapic_lock, flags); + return error; +} + +void +ioapic_disable(unsigned int intr) +{ + unsigned long flags; + + spinlock_lock_intr_save(&ioapic_lock, &flags); + ioapic_disable_intr(ioapic_lookup(intr), intr); + spinlock_unlock_intr_restore(&ioapic_lock, flags); +} diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h new file mode 100644 index 0000000..daebdb1 --- /dev/null +++ b/arch/x86/machine/ioapic.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_IOAPIC_H +#define _KERN_IOAPIC_H + +#include + +/* + * Initialize the ioapic module. + */ +void ioapic_setup(void); + +/* + * Register an I/O APIC controller. + */ +void ioapic_register(unsigned int id, uintptr_t addr, unsigned int gsi_base); + +/* + * Enable/disable an interrupt line. + * + * The given interrupt is routed to the given (cpu, vector) destination. + */ +int ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector); +void ioapic_disable(unsigned int intr); + +#endif /* _KERN_IOAPIC_H */ -- cgit v1.2.3 From 10f2d5f59a37532fb48cf9d6896efa238c9c6eb5 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 26 May 2017 22:20:49 +0200 Subject: kern/intr: new module --- Makefrag.am | 2 + kern/intr.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/intr.h | 69 ++++++++++ 3 files changed, 496 insertions(+) create mode 100644 kern/intr.c create mode 100644 kern/intr.h diff --git a/Makefrag.am b/Makefrag.am index 8a7b8e9..7f74690 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -26,6 +26,8 @@ x15_SOURCES += \ kern/error.c \ kern/error.h \ kern/init.h \ + kern/intr.c \ + kern/intr.h \ kern/kernel.c \ kern/kernel.h \ kern/kmem.c \ diff --git a/kern/intr.c b/kern/intr.c new file mode 100644 index 0000000..0d3a4a8 --- /dev/null +++ b/kern/intr.c @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * XXX Until more hardware is supported, the model used in this implementation + * remains a very simple one, where interrupt controllers are global to the + * system, and each interrupt is targeted at a single processor. + * + * Shared interrupts are supported. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct intr_handler { + struct list node; + intr_handler_fn_t fn; + void *arg; +} __aligned(CPU_L1_SIZE); + +/* + * Interrupt controller. + * + * All members are currently read-only once all controllers have been + * registered. + */ +struct intr_ctl { + struct list node; + const struct intr_ops *ops; + void *priv; + unsigned int first_intr; + unsigned int last_intr; +}; + +/* + * Interrupt table entry, one per vector. + * + * Interrupts must be disabled when accessing an entry. + * + * Each interrupt can be routed to one processor at most. Make each entry + * span a cache line to avoid false sharing. + */ +struct intr_entry { + struct spinlock lock; + struct intr_ctl *ctl; + unsigned int cpu; + struct list handlers; +} __aligned(CPU_L1_SIZE); + +/* + * Interrupt table. + */ +static struct intr_entry intr_table[INTR_TABLE_SIZE]; + +/* + * List of registered controllers. + */ +static struct list intr_ctls; + +static struct kmem_cache intr_handler_cache; + +/* + * Next processor to route interrupts to. + * + * TODO Replace the simple current round-robin policy with a better one. + */ +static unsigned int intr_next_cpu; + +static unsigned int +intr_select_cpu(void) +{ + return atomic_fetch_add(&intr_next_cpu, 1, ATOMIC_RELAXED) % cpu_count(); +} + +static int +intr_handler_create(struct intr_handler **handlerp, + intr_handler_fn_t fn, void *arg) +{ + struct intr_handler *handler; + + handler = kmem_cache_alloc(&intr_handler_cache); + + if (handler == NULL) { + return ERROR_NOMEM; + } + + handler->fn = fn; + handler->arg = arg; + *handlerp = handler; + return 0; +} + +static void +intr_handler_destroy(struct intr_handler *handler) +{ + kmem_cache_free(&intr_handler_cache, handler); +} + +static bool +intr_handler_match(const struct intr_handler *handler, intr_handler_fn_t fn) +{ + return handler->fn == fn; +} + +static int +intr_handler_run(struct intr_handler *handler) +{ + return handler->fn(handler->arg); +} + +static struct intr_ctl * __init +intr_ctl_create(const struct intr_ops *ops, void *priv, + unsigned int first_intr, unsigned int last_intr) +{ + struct intr_ctl *ctl; + + assert(ops != NULL); + assert(first_intr < last_intr); + + ctl = kmem_alloc(sizeof(*ctl)); + + if (ctl == NULL) { + panic("intr: unable to allocate memory for controller"); + } + + ctl->ops = ops; + ctl->priv = priv; + ctl->first_intr = first_intr; + ctl->last_intr = last_intr; + return ctl; +} + +static bool +intr_ctl_has_intr(const struct intr_ctl *ctl, unsigned int intr) +{ + return ((intr >= ctl->first_intr) && (intr <= ctl->last_intr)); +} + +static void +intr_ctl_enable(struct intr_ctl *ctl, unsigned int intr, unsigned int cpu) +{ + ctl->ops->enable(ctl->priv, intr, cpu); +} + +static void +intr_ctl_disable(struct intr_ctl *ctl, unsigned int intr) +{ + ctl->ops->disable(ctl->priv, intr); +} + +static void +intr_ctl_eoi(struct intr_ctl *ctl, unsigned int intr) +{ + ctl->ops->eoi(ctl->priv, intr); +} + +static struct intr_ctl * +intr_lookup_ctl(unsigned int intr) +{ + struct intr_ctl *ctl; + + list_for_each_entry(&intr_ctls, ctl, node) { + if (intr_ctl_has_intr(ctl, intr)) { + return ctl; + } + } + + return NULL; +} + +static void __init +intr_entry_init(struct intr_entry *entry) +{ + spinlock_init(&entry->lock); + list_init(&entry->handlers); +} + +static bool +intr_entry_empty(const struct intr_entry *entry) +{ + return list_empty(&entry->handlers); +} + +static unsigned int +intr_entry_get_intr(const struct intr_entry *entry) +{ + size_t id; + + id = entry - intr_table; + assert(id < ARRAY_SIZE(intr_table)); + return id; +} + +static void +intr_entry_enable(struct intr_entry *entry, struct intr_ctl *ctl, unsigned int cpu) +{ + entry->ctl = ctl; + entry->cpu = cpu; + intr_ctl_enable(entry->ctl, intr_entry_get_intr(entry), cpu); +} + +static void +intr_entry_disable(struct intr_entry *entry) +{ + intr_ctl_disable(entry->ctl, intr_entry_get_intr(entry)); +} + +static struct intr_handler * +intr_entry_lookup_handler(const struct intr_entry *entry, intr_handler_fn_t fn) +{ + struct intr_handler *handler; + + list_for_each_entry(&entry->handlers, handler, node) { + if (intr_handler_match(handler, fn)) { + return handler; + } + } + + return NULL; +} + +static int +intr_entry_add(struct intr_entry *entry, struct intr_handler *handler) +{ + struct intr_ctl *ctl; + unsigned long flags; + unsigned int cpu; + int error; + + spinlock_lock_intr_save(&entry->lock, &flags); + + if (intr_entry_empty(entry)) { + ctl = intr_lookup_ctl(intr_entry_get_intr(entry)); + + if (ctl == NULL) { + error = ERROR_NODEV; + goto out; + } + + cpu = intr_select_cpu(); + intr_entry_enable(entry, ctl, cpu); + } + + list_insert_tail(&entry->handlers, &handler->node); + error = 0; + +out: + spinlock_unlock_intr_restore(&entry->lock, flags); + + return error; +} + +static struct intr_handler * +intr_entry_remove(struct intr_entry *entry, intr_handler_fn_t fn) +{ + struct intr_handler *handler; + unsigned long flags; + + spinlock_lock_intr_save(&entry->lock, &flags); + + handler = intr_entry_lookup_handler(entry, fn); + + if (handler == NULL) { + goto out; + } + + list_remove(&handler->node); + + if (intr_entry_empty(entry)) { + intr_entry_disable(entry); + } + +out: + spinlock_unlock_intr_restore(&entry->lock, flags); + + return handler; +} + +static void +intr_entry_eoi(struct intr_entry *entry, unsigned int intr) +{ + assert(entry->ctl != NULL); + intr_ctl_eoi(entry->ctl, intr); +} + +static struct intr_entry * +intr_get_entry(unsigned int intr) +{ + assert(intr < ARRAY_SIZE(intr_table)); + return &intr_table[intr]; +} + +void __init +intr_setup(void) +{ + unsigned int i; + + list_init(&intr_ctls); + kmem_cache_init(&intr_handler_cache, "intr_handler", + sizeof(struct intr_handler), alignof(struct intr_handler), + NULL, 0); + + for (i = 0; i < ARRAY_SIZE(intr_table); i++) { + intr_entry_init(intr_get_entry(i)); + } +} + +static void __init +intr_check_range(unsigned int first_intr, unsigned int last_intr) +{ + struct intr_ctl *ctl; + unsigned int i; + + list_for_each_entry(&intr_ctls, ctl, node) { + for (i = first_intr; i <= last_intr; i++) { + if (intr_ctl_has_intr(ctl, i)) { + panic("intr: controller range conflict"); + } + } + } +} + +void __init +intr_register_ctl(const struct intr_ops *ops, void *priv, + unsigned int first_intr, unsigned int last_intr) +{ + struct intr_ctl *ctl; + + ctl = intr_ctl_create(ops, priv, first_intr, last_intr); + intr_check_range(first_intr, last_intr); + list_insert_tail(&intr_ctls, &ctl->node); +} + +int +intr_register(unsigned int intr, intr_handler_fn_t fn, void *arg) +{ + struct intr_handler *handler; + int error; + + error = intr_handler_create(&handler, fn, arg); + + if (error) { + return error; + } + + error = intr_entry_add(intr_get_entry(intr), handler); + + if (error) { + goto error; + } + + return 0; + +error: + intr_handler_destroy(handler); + return error; +} + +void +intr_unregister(unsigned int intr, intr_handler_fn_t fn) +{ + struct intr_handler *handler; + + handler = intr_entry_remove(intr_get_entry(intr), fn); + + if (handler == NULL) { + printf("intr: warning: attempting to unregister unknown handler\n"); + return; + } + + intr_handler_destroy(handler); +} + +void +intr_handle(unsigned int intr) +{ + struct intr_handler *handler; + struct intr_entry *entry; + int error; + + assert(!cpu_intr_enabled()); + assert(thread_interrupted()); + + entry = intr_get_entry(intr); + + spinlock_lock(&entry->lock); + + intr_entry_eoi(entry, intr); + + list_for_each_entry(&entry->handlers, handler, node) { + error = intr_handler_run(handler); + + if (!error) { + break; + } + } + + spinlock_unlock(&entry->lock); +} diff --git a/kern/intr.h b/kern/intr.h new file mode 100644 index 0000000..1cdab5e --- /dev/null +++ b/kern/intr.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Machine-independent interrupt management. + */ + +#ifndef _KERN_INTR_H +#define _KERN_INTR_H + +/* + * Type for interrupt handler functions. + * + * Return codes : + * - 0 Interrupt successfully handled + * - ERROR_AGAIN Spurious interrupt + */ +typedef int (*intr_handler_fn_t)(void *arg); + +/* + * Operations of an interrupt controller. + * + * Operations for interrupts targeting the same processor are serialized. + */ +struct intr_ops { + void (*enable)(void *priv, unsigned int intr, unsigned int cpu); + void (*disable)(void *priv, unsigned int intr); + void (*eoi)(void *priv, unsigned int intr); +}; + +/* + * Initialize the intr module. + */ +void intr_setup(void); + +/* + * Register an interrupt controller. + * + * This function isn't thread-safe and can only be called during system + * initialization. + */ +void intr_register_ctl(const struct intr_ops *ops, void *priv, + unsigned int first_intr, unsigned int last_intr); + +/* + * Register/unregister an interrupt handler. + */ +int intr_register(unsigned int intr, intr_handler_fn_t fn, void *arg); +void intr_unregister(unsigned int intr, intr_handler_fn_t fn); + +/* + * Handle an interrupt. + */ +void intr_handle(unsigned int intr); + +#endif /* _KERN_INTR_H */ -- cgit v1.2.3 From cab8846284996df401c5bdaa0088b224e076b510 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 26 May 2017 22:39:31 +0200 Subject: x86: use the new intr module In particular, the pic and ioapic modules register their respective devices as interrupt controllers. Selection between the legacy XT-PIC and the modern APIC system is made on the availability of ACPI, disregarding the multiprocessor specification entirely. The uart driver is also updated to register devices interrupt handlers. --- arch/x86/machine/acpimp.c | 1 - arch/x86/machine/boot.c | 7 +- arch/x86/machine/cpu.c | 9 ++- arch/x86/machine/ioapic.c | 182 +++++++++++++++++----------------------------- arch/x86/machine/ioapic.h | 13 ---- arch/x86/machine/lapic.c | 23 +++++- arch/x86/machine/lapic.h | 14 +++- arch/x86/machine/param.h | 5 ++ arch/x86/machine/pic.c | 146 ++++++++++++++++++++++++++++++++----- arch/x86/machine/uart.c | 39 +++++++++- arch/x86/machine/uart.h | 10 +++ 11 files changed, 293 insertions(+), 156 deletions(-) diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index 4e98683..bba2b8c 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -495,7 +495,6 @@ acpimp_load_madt(void) assert(table != NULL); madt = structof(table, struct acpimp_madt, header); lapic_setup(madt->lapic_addr); - ioapic_setup(); is_bsp = 1; /* diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 1830600..0df9674 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -468,7 +469,7 @@ boot_main(void) thread_bootstrap(); console_setup(); cga_setup(); - uart_setup(); + uart_bootstrap(); printf_setup(); boot_show_version(); arg_info(); @@ -481,8 +482,10 @@ boot_main(void) vm_setup(); boot_save_data(); biosmem_free_usable(); - pic_setup(); + intr_setup(); cpu_mp_probe(); + pic_setup(); + uart_setup(); kernel_main(); /* Never reached */ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index d62b68f..fbc1f95 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -650,9 +650,14 @@ cpu_mp_probe(void) error = acpimp_setup(); - /* TODO Support UP with legacy PIC */ if (error) { - panic("cpu: ACPI required to initialize local APIC"); + /* + * For the sake of simplicity, it has been decided to ignore legacy + * specifications such as the multiprocessor specification, and use + * ACPI only. If ACPI is unavailable, consider the APIC system to + * be missing and fall back to using the legaxy XT-PIC. + */ + lapic_setup_unused(); } printf("cpu: %u processor(s) configured\n", cpu_count()); diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 48faf8d..4517c3f 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ struct ioapic_map { }; struct ioapic { + struct spinlock lock; unsigned int id; unsigned int version; volatile struct ioapic_map *map; @@ -62,23 +64,6 @@ struct ioapic { unsigned int last_intr; }; -static struct ioapic *ioapic_devs; -static unsigned int ioapic_nr_devs; - -/* - * Global lock. - * - * Interrupts must be disabled when holding this lock. - */ -static struct spinlock ioapic_lock; - -static struct ioapic * -ioapic_get(unsigned int id) -{ - assert(id < ioapic_nr_devs); - return &ioapic_devs[id]; -} - static uint32_t ioapic_read(struct ioapic *ioapic, uint8_t reg) { @@ -107,61 +92,26 @@ ioapic_write_entry_high(struct ioapic *ioapic, unsigned int id, uint32_t value) ioapic_write(ioapic, IOAPIC_REG_IOREDTBL + (id * 2) + 1, value); } -static bool -ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) -{ - return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); -} - -static unsigned int -ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) -{ - assert(ioapic_has_intr(ioapic, intr)); - return intr - ioapic->first_intr; -} - -static void -ioapic_enable_intr(struct ioapic *ioapic, unsigned int intr, - unsigned int cpu, unsigned int vector) -{ - unsigned int id; - - id = ioapic_compute_id(ioapic, intr); - ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); - ioapic_write_entry_low(ioapic, id, vector); -} - -static void -ioapic_disable_intr(struct ioapic *ioapic, unsigned int intr) -{ - unsigned int id; - - id = ioapic_compute_id(ioapic, intr); - ioapic_write_entry_low(ioapic, id, IOAPIC_ENTLOW_INTRMASK); -} - static void ioapic_intr(struct trap_frame *frame) { - lapic_eoi(); - printf("ioapic: cpu:%u vector:%lu\n", cpu_id(), frame->vector); -} - -void __init -ioapic_setup(void) -{ - ioapic_devs = NULL; - ioapic_nr_devs = 0; - spinlock_init(&ioapic_lock); + intr_handle(frame->vector - TRAP_INTR_FIRST); } -static void __init -ioapic_init(struct ioapic *ioapic, unsigned int id, - uintptr_t addr, unsigned int intr_base) +static struct ioapic * __init +ioapic_create(unsigned int id, uintptr_t addr, unsigned int intr_base) { + struct ioapic *ioapic; unsigned int i, nr_intrs; uint32_t value; + ioapic = kmem_alloc(sizeof(*ioapic)); + + if (ioapic == NULL) { + panic("ioapic: unable to allocate memory for controller"); + } + + spinlock_init(&ioapic->lock); ioapic->id = id; ioapic->first_intr = intr_base; @@ -182,84 +132,82 @@ ioapic_init(struct ioapic *ioapic, unsigned int id, panic("ioapic: invalid interrupt range"); } - printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, - ioapic->version, ioapic->first_intr, ioapic->last_intr); - for (i = ioapic->first_intr; i < ioapic->last_intr; i++) { trap_register(TRAP_INTR_FIRST + i, ioapic_intr); } -} - -void __init -ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) -{ - struct ioapic *tmp; - - spinlock_lock(&ioapic_lock); - - tmp = kmem_alloc((ioapic_nr_devs + 1) * sizeof(*ioapic_devs)); - if (tmp == NULL) { - panic("ioapic: unable to allocate memory for device"); - } + printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, + ioapic->version, ioapic->first_intr, ioapic->last_intr); - memcpy(tmp, ioapic_devs, ioapic_nr_devs); - kmem_free(ioapic_devs, ioapic_nr_devs * sizeof(*ioapic_devs)); - ioapic_devs = tmp; - ioapic_nr_devs++; + return ioapic; +} - ioapic_init(ioapic_get(ioapic_nr_devs - 1), id, addr, intr_base); +static bool +ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) +{ + return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); +} - spinlock_unlock(&ioapic_lock); +static unsigned int +ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) +{ + assert(ioapic_has_intr(ioapic, intr)); + return intr - ioapic->first_intr; } -static struct ioapic * -ioapic_lookup(unsigned int intr) +static void +ioapic_enable(void *priv, unsigned int intr, unsigned int cpu) { struct ioapic *ioapic; - unsigned int i; - - for (i = 0; i < ioapic_nr_devs; i++) { - ioapic = &ioapic_devs[i]; + unsigned long flags; + unsigned int id; - if ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)) { - return ioapic; - } - } + ioapic = priv; + id = ioapic_compute_id(ioapic, intr); - return NULL; + spinlock_lock_intr_save(&ioapic->lock, &flags); + ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); + ioapic_write_entry_low(ioapic, id, TRAP_INTR_FIRST + intr); + spinlock_unlock_intr_restore(&ioapic->lock, flags); } -int -ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector) +static void +ioapic_disable(void *priv, unsigned int intr) { struct ioapic *ioapic; unsigned long flags; - int error; - - spinlock_lock_intr_save(&ioapic_lock, &flags); + unsigned int id; - ioapic = ioapic_lookup(intr); + ioapic = priv; + id = ioapic_compute_id(ioapic, intr); - if (ioapic == NULL) { - error = ERROR_NODEV; - goto out; - } + spinlock_lock_intr_save(&ioapic->lock, &flags); + ioapic_write_entry_low(ioapic, id, IOAPIC_ENTLOW_INTRMASK); + spinlock_unlock_intr_restore(&ioapic->lock, flags); +} - ioapic_enable_intr(ioapic, intr, cpu, vector); - error = 0; +static void +ioapic_eoi(void *priv, unsigned int intr) +{ + (void)priv; + (void)intr; -out: - spinlock_unlock_intr_restore(&ioapic_lock, flags); - return error; + lapic_eoi(); } -void -ioapic_disable(unsigned int intr) +static const struct intr_ops ioapic_ops = { + .enable = ioapic_enable, + .disable = ioapic_disable, + .eoi = ioapic_eoi, +}; + +void __init +ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) { - unsigned long flags; + struct ioapic *ioapic; - spinlock_lock_intr_save(&ioapic_lock, &flags); - ioapic_disable_intr(ioapic_lookup(intr), intr); - spinlock_unlock_intr_restore(&ioapic_lock, flags); + ioapic = ioapic_create(id, addr, intr_base); + intr_register_ctl(&ioapic_ops, ioapic, + ioapic->first_intr, ioapic->last_intr); } + diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h index daebdb1..4d22085 100644 --- a/arch/x86/machine/ioapic.h +++ b/arch/x86/machine/ioapic.h @@ -20,22 +20,9 @@ #include -/* - * Initialize the ioapic module. - */ -void ioapic_setup(void); - /* * Register an I/O APIC controller. */ void ioapic_register(unsigned int id, uintptr_t addr, unsigned int gsi_base); -/* - * Enable/disable an interrupt line. - * - * The given interrupt is routed to the given (cpu, vector) destination. - */ -int ioapic_enable(unsigned int intr, unsigned int cpu, unsigned int vector); -void ioapic_disable(unsigned int intr); - #endif /* _KERN_IOAPIC_H */ diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index a978da1..444e661 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Richard Braun. + * Copyright (c) 2011-2017 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 @@ -15,10 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include +#include #include #include #include @@ -182,6 +184,9 @@ static volatile struct lapic_map *lapic_map __read_mostly; */ static uint32_t lapic_bus_freq __read_mostly; +static bool lapic_initialized __initdata; +static bool lapic_is_unused __initdata; + static uint32_t lapic_read(const volatile struct lapic_register *r) { @@ -237,6 +242,20 @@ lapic_setup_registers(void) lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); } +bool __init +lapic_unused(void) +{ + assert(lapic_initialized); + return lapic_is_unused; +} + +void __init +lapic_setup_unused(void) +{ + lapic_initialized = true; + lapic_is_unused = true; +} + void __init lapic_setup(uint32_t map_addr) { @@ -256,6 +275,8 @@ lapic_setup(uint32_t map_addr) lapic_setup_registers(); lapic_setup_timer(); + + lapic_initialized = true; } void __init diff --git a/arch/x86/machine/lapic.h b/arch/x86/machine/lapic.h index 802d125..ff9c893 100644 --- a/arch/x86/machine/lapic.h +++ b/arch/x86/machine/lapic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012 Richard Braun. + * Copyright (c) 2011-2017 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 @@ -18,6 +18,7 @@ #ifndef _X86_LAPIC_H #define _X86_LAPIC_H +#include #include #include @@ -27,6 +28,17 @@ */ void lapic_eoi(void); +/* + * Report whether the local APIC is actually used or not. + */ +bool lapic_unused(void); + +/* + * Initialize the lapic module for the sole purpose of reporting that + * it's actually not used. + */ +void lapic_setup_unused(void); + /* * Set up the lapic module. */ diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index e5e0f0a..6ee11cc 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -82,6 +82,11 @@ */ #define STACK_SIZE PAGE_SIZE +/* + * Maximum number of available interrupts. + */ +#define INTR_TABLE_SIZE 256 + /* * Virtual memory properties. */ diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index 412135c..fed8c97 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -19,9 +19,11 @@ #include #include +#include #include -#include #include +#include +#include #include #include @@ -48,29 +50,43 @@ #define PIC_SLAVE_INTR 2 #define PIC_SPURIOUS_INTR 7 #define PIC_NR_INTRS 8 +#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1) -void __init -pic_setup(void) +static unsigned int pic_nr_slave_intrs; + +static uint8_t pic_master_mask; +static uint8_t pic_slave_mask; + +static bool +pic_is_slave_intr(unsigned int intr) { - /* ICW 1 - State that ICW 4 will be sent */ - io_write_byte(PIC_MASTER_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); - io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + assert(intr <= PIC_MAX_INTR); + return (intr >= PIC_NR_INTRS); +} - /* ICW 2 */ - io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); - io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); +static void +pic_inc_slave_intrs(void) +{ + if (pic_nr_slave_intrs == 0) { + pic_master_mask |= 1 << PIC_SLAVE_INTR; + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } - /* ICW 3 - Set up cascading */ - io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); - io_write_byte(PIC_SLAVE_IMR, PIC_SLAVE_INTR); + pic_nr_slave_intrs++; + assert(pic_nr_slave_intrs != 0); +} - /* ICW 4 - Set 8086 mode */ - io_write_byte(PIC_MASTER_IMR, PIC_ICW4_8086); - io_write_byte(PIC_SLAVE_IMR, PIC_ICW4_8086); +static void +pic_dec_slave_intrs(void) +{ + assert(pic_nr_slave_intrs != 0); - /* OCW 1 - Mask all interrupts */ - io_write_byte(PIC_MASTER_IMR, 0xff); - io_write_byte(PIC_SLAVE_IMR, 0xff); + pic_nr_slave_intrs--; + + if (pic_nr_slave_intrs == 0) { + pic_master_mask &= ~(1 << PIC_SLAVE_INTR); + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } } static void @@ -90,6 +106,100 @@ pic_read_isr(uint16_t port) return io_read_byte(port); } +static void +pic_ops_enable(void *priv, unsigned int intr, unsigned int cpu) +{ + (void)priv; + (void)cpu; + + if (pic_is_slave_intr(intr)) { + pic_slave_mask &= ~(1 << (intr - PIC_NR_INTRS)); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + pic_inc_slave_intrs(); + } else { + pic_master_mask &= ~(1 << intr); + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } +} + +static void +pic_ops_disable(void *priv, unsigned int intr) +{ + (void)priv; + + if (pic_is_slave_intr(intr)) { + pic_dec_slave_intrs(); + pic_slave_mask |= 1 << (intr - PIC_NR_INTRS); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + } else { + pic_master_mask |= 1 << intr; + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + } +} + +static void +pic_ops_eoi(void *priv, unsigned int intr) +{ + (void)priv; + pic_eoi(intr); +} + +static const struct intr_ops pic_ops = { + .enable = pic_ops_enable, + .disable = pic_ops_disable, + .eoi = pic_ops_eoi, +}; + +static void +pic_intr(struct trap_frame *frame) +{ + intr_handle(frame->vector - TRAP_INTR_FIRST); +} + +static void __init +pic_register(void) +{ + unsigned int intr; + + intr_register_ctl(&pic_ops, NULL, 0, PIC_MAX_INTR); + + for (intr = 0; intr <= PIC_MAX_INTR; intr++) { + trap_register(TRAP_INTR_FIRST + intr, pic_intr); + } +} + +void __init +pic_setup(void) +{ + pic_nr_slave_intrs = 0; + pic_master_mask = 0xff; + pic_slave_mask = 0xff; + + /* ICW 1 - State that ICW 4 will be sent */ + io_write_byte(PIC_MASTER_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); + + /* ICW 2 */ + io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); + io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); + + /* ICW 3 - Set up cascading */ + io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); + io_write_byte(PIC_SLAVE_IMR, PIC_SLAVE_INTR); + + /* ICW 4 - Set 8086 mode */ + io_write_byte(PIC_MASTER_IMR, PIC_ICW4_8086); + io_write_byte(PIC_SLAVE_IMR, PIC_ICW4_8086); + + /* OCW 1 - Mask all interrupts */ + io_write_byte(PIC_MASTER_IMR, pic_master_mask); + io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); + + if (lapic_unused()) { + pic_register(); + } +} + void pic_spurious_intr(struct trap_frame *frame) { diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index d70cc41..86ef1e3 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -183,7 +184,7 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) } void __init -uart_setup(void) +uart_bootstrap(void) { const uint16_t *ptr; size_t i; @@ -199,6 +200,42 @@ uart_setup(void) } } +static int +uart_intr(void *arg) +{ + struct uart *uart; + uint8_t byte; + + uart = arg; + byte = uart_read(uart, UART_REG_DAT); + printf("uart: intr:%u byte:%hhu (%c)\n", uart->intr, byte, byte); + return 0; +} + +void __init +uart_setup(void) +{ + struct uart *uart; + int error; + size_t i; + + for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { + uart = uart_get_dev(i); + + if (uart->port == 0) { + continue; + } + + error = intr_register(uart->intr, uart_intr, uart); + + if (error) { + printf("uart%zu: unable to register interrupt %u\n", i, uart->intr); + } + + uart_write(uart, UART_REG_IER, 1); + } +} + void __init uart_info(void) { diff --git a/arch/x86/machine/uart.h b/arch/x86/machine/uart.h index 9cb7cbf..60b195c 100644 --- a/arch/x86/machine/uart.h +++ b/arch/x86/machine/uart.h @@ -21,8 +21,18 @@ #ifndef _X86_UART_H #define _X86_UART_H +/* + * Early initialization of the uart module. + * + * Devices may only be used to report diagnostics until initialization + * is completed. + */ +void uart_bootstrap(void); + /* * Initialize the uart module. + * + * On return, devices may be used for both input and output, using interrupts. */ void uart_setup(void); -- cgit v1.2.3 From 7770b89253b03dd68084139914f82290d57eaca6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 15:45:44 +0200 Subject: x86/ioapic: name devices based on registration order --- arch/x86/machine/ioapic.c | 13 +++++++++---- arch/x86/machine/ioapic.h | 3 ++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 4517c3f..66178b3 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -58,12 +58,15 @@ struct ioapic_map { struct ioapic { struct spinlock lock; unsigned int id; + unsigned int apic_id; unsigned int version; volatile struct ioapic_map *map; unsigned int first_intr; unsigned int last_intr; }; +static unsigned int ioapic_nr_devs; + static uint32_t ioapic_read(struct ioapic *ioapic, uint8_t reg) { @@ -99,7 +102,7 @@ ioapic_intr(struct trap_frame *frame) } static struct ioapic * __init -ioapic_create(unsigned int id, uintptr_t addr, unsigned int intr_base) +ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) { struct ioapic *ioapic; unsigned int i, nr_intrs; @@ -112,7 +115,8 @@ ioapic_create(unsigned int id, uintptr_t addr, unsigned int intr_base) } spinlock_init(&ioapic->lock); - ioapic->id = id; + ioapic->id = ioapic_nr_devs; + ioapic->apic_id = apic_id; ioapic->first_intr = intr_base; ioapic->map = vm_kmem_map_pa(addr, sizeof(*ioapic->map), NULL, NULL); @@ -139,6 +143,7 @@ ioapic_create(unsigned int id, uintptr_t addr, unsigned int intr_base) printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, ioapic->version, ioapic->first_intr, ioapic->last_intr); + ioapic_nr_devs++; return ioapic; } @@ -202,11 +207,11 @@ static const struct intr_ops ioapic_ops = { }; void __init -ioapic_register(unsigned int id, uintptr_t addr, unsigned int intr_base) +ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) { struct ioapic *ioapic; - ioapic = ioapic_create(id, addr, intr_base); + ioapic = ioapic_create(apic_id, addr, intr_base); intr_register_ctl(&ioapic_ops, ioapic, ioapic->first_intr, ioapic->last_intr); } diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h index 4d22085..d508904 100644 --- a/arch/x86/machine/ioapic.h +++ b/arch/x86/machine/ioapic.h @@ -23,6 +23,7 @@ /* * Register an I/O APIC controller. */ -void ioapic_register(unsigned int id, uintptr_t addr, unsigned int gsi_base); +void ioapic_register(unsigned int apic_id, uintptr_t addr, + unsigned int gsi_base); #endif /* _KERN_IOAPIC_H */ -- cgit v1.2.3 From 7fba7f7b2fe1106e442cf4150c1e6f7ffa4492a3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 15:48:35 +0200 Subject: x86/pic: handle spurious interrupts Support was lost during the trap module rework. --- arch/x86/machine/pic.c | 67 ++++++++++++++++++++++++++++++-------------------- arch/x86/machine/pic.h | 7 +----- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index fed8c97..9084abc 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,9 @@ static unsigned int pic_nr_slave_intrs; static uint8_t pic_master_mask; static uint8_t pic_slave_mask; +static uint8_t pic_master_spurious_intr; +static uint8_t pic_slave_spurious_intr; + static bool pic_is_slave_intr(unsigned int intr) { @@ -168,9 +172,37 @@ pic_register(void) } } +static int +pic_spurious_intr(void *arg) +{ + uint8_t intr, isr; + + intr = *(const uint8_t *)arg; + + if (arg == &pic_master_spurious_intr) { + isr = pic_read_isr(PIC_MASTER_CMD); + + if (isr & (1 << PIC_SPURIOUS_INTR)) { + panic("pic: real interrupt %hhu", intr); + } + } else { + isr = pic_read_isr(PIC_SLAVE_CMD); + + if (isr & (1 << PIC_SPURIOUS_INTR)) { + panic("pic: real interrupt %hhu", intr); + } + + pic_eoi(PIC_SLAVE_INTR); + } + + return 0; +} + void __init pic_setup(void) { + int error; + pic_nr_slave_intrs = 0; pic_master_mask = 0xff; pic_slave_mask = 0xff; @@ -198,31 +230,14 @@ pic_setup(void) if (lapic_unused()) { pic_register(); } -} - -void -pic_spurious_intr(struct trap_frame *frame) -{ - unsigned long intr; - uint8_t isr; - intr = frame->vector - TRAP_INTR_FIRST; - assert((intr == PIC_SPURIOUS_INTR) - || (intr == (PIC_NR_INTRS + PIC_SPURIOUS_INTR))); + pic_master_spurious_intr = PIC_SPURIOUS_INTR; + error = intr_register(pic_master_spurious_intr, pic_spurious_intr, + &pic_master_spurious_intr); + error_check(error, __func__); - if (intr == PIC_SPURIOUS_INTR) { - isr = pic_read_isr(PIC_MASTER_CMD); - - if (isr & (1 << PIC_SPURIOUS_INTR)) { - panic("pic: real interrupt %lu", intr); - } - } else { - isr = pic_read_isr(PIC_SLAVE_CMD); - - if (isr & (1 << PIC_SPURIOUS_INTR)) { - panic("pic: real interrupt %lu", intr); - } - - pic_eoi(PIC_SLAVE_INTR); - } + pic_slave_spurious_intr = PIC_NR_INTRS + PIC_SPURIOUS_INTR; + error = intr_register(pic_slave_spurious_intr, pic_spurious_intr, + &pic_slave_spurious_intr); + error_check(error, __func__); } diff --git a/arch/x86/machine/pic.h b/arch/x86/machine/pic.h index 3dc13d4..9da75b7 100644 --- a/arch/x86/machine/pic.h +++ b/arch/x86/machine/pic.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -25,9 +25,4 @@ */ void pic_setup(void); -/* - * Interrupt handlers. - */ -void pic_spurious_intr(struct trap_frame *frame); - #endif /* _X86_PIC_H */ -- cgit v1.2.3 From a72801f115413cd3eab46857545ab527f31abb2b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 16:32:01 +0200 Subject: kern/console: implement console selection --- arch/x86/machine/cga.c | 2 +- arch/x86/machine/uart.c | 14 +++++++++++- kern/console.c | 61 +++++++++++++++++++++++++++++++++++++------------ kern/console.h | 19 +++++++++------ 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 86a661c..3b217ca 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -191,6 +191,6 @@ cga_setup(void) cga_cursor = cga_get_cursor_position(); - console_init(&cga_console, cga_console_putc); + console_init(&cga_console, "cga", cga_console_putc); console_register(&cga_console); } diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 86ef1e3..f17bc66 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -67,6 +67,16 @@ static struct uart uart_devs[UART_MAX_DEVS]; static uint16_t uart_intrs[UART_MAX_DEVS] = { 4, 3, 4, 3 }; +static size_t +uart_get_id(const struct uart *uart) +{ + size_t id; + + id = uart - uart_devs; + assert(id < ARRAY_SIZE(uart_devs)); + return id; +} + static uint16_t uart_get_addr(const struct uart *uart, uint16_t reg) { @@ -163,6 +173,7 @@ uart_console_putc(struct console *console, char c) static void __init uart_init(struct uart *uart, uint16_t port, uint16_t intr) { + char name[CONSOLE_NAME_SIZE]; uint8_t byte; uart->port = port; @@ -179,7 +190,8 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S | UART_LCR_BEN; uart_write(uart, UART_REG_LCR, byte); - console_init(&uart->console, uart_console_putc); + snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); + console_init(&uart->console, name, uart_console_putc); console_register(&uart->console); } diff --git a/kern/console.c b/kern/console.c index 138a424..eb1306f 100644 --- a/kern/console.c +++ b/kern/console.c @@ -15,16 +15,48 @@ * along with this program. If not, see . */ +#include #include +#include +#include +#include #include #include #include #include #include +/* + * Registered consoles. + */ static struct list console_devs; -static struct spinlock console_lock; + +/* + * Active console device. + */ +static struct console *console_dev; + +static const char *console_name __initdata; + +static bool __init +console_name_match(const char *name) +{ + if (console_name == NULL) { + return true; + } + + return (strcmp(console_name, name) == 0); +} + +void __init +console_init(struct console *console, const char *name, + console_putc_fn putc) +{ + spinlock_init(&console->lock); + console->putc = putc; + strlcpy(console->name, name, sizeof(console->name)); +} static void console_putc(struct console *console, char c) @@ -40,30 +72,31 @@ void __init console_setup(void) { list_init(&console_devs); - spinlock_init(&console_lock); + console_name = arg_value("console"); } void __init console_register(struct console *console) { - assert(console->putc != NULL); - - spinlock_lock(&console_lock); list_insert_tail(&console_devs, &console->node); - spinlock_unlock(&console_lock); + + if ((console_dev == NULL) && console_name_match(console->name)) { + console_dev = console; + } + + printf("console: %s registered\n", console->name); + + if (console == console_dev) { + printf("console: %s selected as active console\n", console->name); + } } void console_write_char(char c) { - struct console *console; - unsigned long flags; - - spinlock_lock_intr_save(&console_lock, &flags); - - list_for_each_entry(&console_devs, console, node) { - console_putc(console, c); + if (console_dev == NULL) { + return; } - spinlock_unlock_intr_restore(&console_lock, flags); + console_putc(console_dev, c); } diff --git a/kern/console.h b/kern/console.h index 8fdef16..3087b71 100644 --- a/kern/console.h +++ b/kern/console.h @@ -31,25 +31,27 @@ struct console; */ typedef void (*console_putc_fn)(struct console *console, char c); +#define CONSOLE_NAME_SIZE 16 + /* * Console device. * * This structure should be embedded in the hardware-specific console * objects. Calls to console operations are all serialized by this module - * for each device. + * for each device. Interrupts are disabled when calling operations. */ struct console { struct spinlock lock; struct list node; console_putc_fn putc; + char name[CONSOLE_NAME_SIZE]; }; -static inline void -console_init(struct console *console, console_putc_fn putc) -{ - spinlock_init(&console->lock); - console->putc = putc; -} +/* + * Console initialization. + */ +void console_init(struct console *console, const char *name, + console_putc_fn putc); /* * Initialize the console module. @@ -60,6 +62,9 @@ void console_setup(void); * Register a console device. * * The given console must be initialized before calling this function. + * + * This function isn't thread-safe and can only be called during system + * initialization. */ void console_register(struct console *console); -- cgit v1.2.3 From df6e8f87ad6800ecb348f11b01a42db48237b371 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:05:16 +0200 Subject: kern/cbuf: new module --- Makefrag.am | 2 ++ kern/cbuf.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ kern/cbuf.h | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 kern/cbuf.c create mode 100644 kern/cbuf.h diff --git a/Makefrag.am b/Makefrag.am index 7f74690..243c224 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -15,6 +15,8 @@ x15_SOURCES += \ kern/bitmap.c \ kern/bitmap.h \ kern/bitmap_i.h \ + kern/cbuf.c \ + kern/cbuf.h \ kern/condition.c \ kern/condition.h \ kern/condition_types.h \ diff --git a/kern/cbuf.c b/kern/cbuf.c new file mode 100644 index 0000000..17369af --- /dev/null +++ b/kern/cbuf.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 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 3 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 + +/* Negative close to 0 so that an overflow occurs early */ +#define CBUF_INIT_INDEX ((unsigned long)-500) + +void +cbuf_init(struct cbuf *cbuf, char *buf, unsigned long capacity) +{ + assert(ISP2(capacity)); + + cbuf->buf = buf; + cbuf->capacity = capacity; + cbuf->start = CBUF_INIT_INDEX; + cbuf->end = cbuf->start; +} + +static unsigned long +cbuf_index(const struct cbuf *cbuf, unsigned long abs_index) +{ + return abs_index & (cbuf->capacity - 1); +} + +void +cbuf_push(struct cbuf *cbuf, char byte) +{ + cbuf->buf[cbuf_index(cbuf, cbuf->end)] = byte; + cbuf->end++; + + /* Mind integer overflows */ + if (cbuf_size(cbuf) > cbuf->capacity) { + cbuf->start = cbuf->end - cbuf->capacity; + } +} + +int +cbuf_pop(struct cbuf *cbuf, char *bytep) +{ + if (cbuf_size(cbuf) == 0) { + return ERROR_AGAIN; + } + + *bytep = cbuf->buf[cbuf_index(cbuf, cbuf->start)]; + cbuf->start++; + return 0; +} + +int +cbuf_read(const struct cbuf *cbuf, unsigned long index, char *bytep) +{ + /* Mind integer overflows */ + if ((cbuf->end - index - 1) >= cbuf_size(cbuf)) { + return ERROR_INVAL; + } + + *bytep = cbuf->buf[cbuf_index(cbuf, index)]; + return 0; +} diff --git a/kern/cbuf.h b/kern/cbuf.h new file mode 100644 index 0000000..3ea3841 --- /dev/null +++ b/kern/cbuf.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015 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 3 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 . + * + * + * Circular character buffer. + */ + +#ifndef _KERN_CBUF_H +#define _KERN_CBUF_H + +/* + * Circular buffer descriptor. + * + * The buffer capacity must be a power-of-two. Indexes are absolute values + * which can overflow. Their difference cannot exceed the capacity. + */ +struct cbuf { + char *buf; + unsigned long capacity; + unsigned long start; + unsigned long end; +}; + +static inline unsigned long +cbuf_capacity(const struct cbuf *cbuf) +{ + return cbuf->capacity; +} + +static inline unsigned long +cbuf_start(const struct cbuf *cbuf) +{ + return cbuf->start; +} + +static inline unsigned long +cbuf_end(const struct cbuf *cbuf) +{ + return cbuf->end; +} + +static inline unsigned long +cbuf_size(const struct cbuf *cbuf) +{ + return cbuf->end - cbuf->start; +} + +static inline void +cbuf_clear(struct cbuf *cbuf) +{ + cbuf->start = cbuf->end; +} + +/* + * Initialize a circular buffer. + * + * The descriptor is set to use the given buffer for storage. Capacity + * must be a power-of-two. + */ +void cbuf_init(struct cbuf *cbuf, char *buf, unsigned long capacity); + +/* + * Append a byte to a circular buffer. + * + * The end index is incremented. If the buffer is full, the oldest byte + * is overwritten and the start index is updated accordingly. + */ +void cbuf_push(struct cbuf *cbuf, char byte); + +/* + * Read a byte from a circular buffer. + * + * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest + * byte is stored at the bytep address, the start index is incremented, + * and 0 is returned. + */ +int cbuf_pop(struct cbuf *cbuf, char *bytep); + +/* + * Read a byte at a specific location. + * + * If the given index is outside buffer boundaries, ERROR_INVAL is returned. + * Otherwise the byte is stored at the bytep address and 0 is returned. + * The buffer isn't changed by this operation. + */ +int cbuf_read(const struct cbuf *cbuf, unsigned long index, char *bytep); + +#endif /* _KERN_CBUF_H */ -- cgit v1.2.3 From c5680f01485f35c1735cdae08b16b63e300b9f6d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:03:27 +0200 Subject: kern/console: implement input handling --- arch/x86/machine/cga.c | 6 +++- arch/x86/machine/uart.c | 6 +++- include/stdio.h | 4 +++ kern/console.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++--- kern/console.h | 35 ++++++++++++++----- kern/printf.c | 2 +- 6 files changed, 127 insertions(+), 15 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 3b217ca..3f1525c 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -165,6 +165,10 @@ cga_console_putc(struct console *console, char c) cga_write_char(c); } +static const struct console_ops cga_console_ops = { + .putc = cga_console_putc, +}; + void __init cga_setup(void) { @@ -191,6 +195,6 @@ cga_setup(void) cga_cursor = cga_get_cursor_position(); - console_init(&cga_console, "cga", cga_console_putc); + console_init(&cga_console, "cga", &cga_console_ops); console_register(&cga_console); } diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index f17bc66..16b6d92 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -170,6 +170,10 @@ uart_console_putc(struct console *console, char c) uart_write_char(uart_get_from_console(console), c); } +static struct console_ops uart_console_ops = { + .putc = uart_console_putc, +}; + static void __init uart_init(struct uart *uart, uint16_t port, uint16_t intr) { @@ -191,7 +195,7 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) uart_write(uart, UART_REG_LCR, byte); snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); - console_init(&uart->console, name, uart_console_putc); + console_init(&uart->console, name, &uart_console_ops); console_register(&uart->console); } diff --git a/include/stdio.h b/include/stdio.h index a9aed9e..636304c 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -21,4 +21,8 @@ #include #include +#ifndef EOF +#define EOF (-1) +#endif + #endif /* _STDIO_H */ diff --git a/kern/console.c b/kern/console.c index eb1306f..c82e7dc 100644 --- a/kern/console.c +++ b/kern/console.c @@ -22,10 +22,14 @@ #include #include +#include #include #include #include +#include #include +#include +#include /* * Registered consoles. @@ -51,10 +55,14 @@ console_name_match(const char *name) void __init console_init(struct console *console, const char *name, - console_putc_fn putc) + const struct console_ops *ops) { + assert(ops != NULL); + spinlock_init(&console->lock); - console->putc = putc; + console->ops = ops; + cbuf_init(&console->recvbuf, console->buffer, sizeof(console->buffer)); + console->waiter = NULL; strlcpy(console->name, name, sizeof(console->name)); } @@ -64,10 +72,44 @@ console_putc(struct console *console, char c) unsigned long flags; spinlock_lock_intr_save(&console->lock, &flags); - console->putc(console, c); + console->ops->putc(console_dev, c); spinlock_unlock_intr_restore(&console->lock, flags); } +static char +console_getc(struct console *console) +{ + unsigned long flags; + int error; + char c; + + spinlock_lock_intr_save(&console->lock, &flags); + + if (console->waiter != NULL) { + c = EOF; + goto out; + } + + console->waiter = thread_self(); + + for (;;) { + error = cbuf_pop(&console->recvbuf, &c); + + if (!error) { + break; + } + + thread_sleep(&console->lock, console, "consgetc"); + } + + console->waiter = NULL; + +out: + spinlock_unlock_intr_restore(&console->lock, flags); + + return c; +} + void __init console_setup(void) { @@ -78,6 +120,8 @@ console_setup(void) void __init console_register(struct console *console) { + assert(console->ops != NULL); + list_insert_tail(&console_devs, &console->node); if ((console_dev == NULL) && console_name_match(console->name)) { @@ -92,7 +136,28 @@ console_register(struct console *console) } void -console_write_char(char c) +console_intr(struct console *console, char c) +{ + assert(!cpu_intr_enabled()); + + spinlock_lock(&console->lock); + + if (cbuf_size(&console->recvbuf) == cbuf_capacity(&console->recvbuf)) { + goto out; + } + + cbuf_push(&console->recvbuf, c); + + if ((console->waiter != NULL) && (console->waiter != thread_self())) { + thread_wakeup(console->waiter); + } + +out: + spinlock_unlock(&console->lock); +} + +void +console_putchar(char c) { if (console_dev == NULL) { return; @@ -100,3 +165,19 @@ console_write_char(char c) console_putc(console_dev, c); } + +char +console_getchar(void) +{ + char c; + + if (console_dev == NULL) { + c = EOF; + goto out; + } + + c = console_getc(console_dev); + +out: + return c; +} diff --git a/kern/console.h b/kern/console.h index 3087b71..b967c0d 100644 --- a/kern/console.h +++ b/kern/console.h @@ -21,16 +21,18 @@ #ifndef _KERN_CONSOLE_H #define _KERN_CONSOLE_H +#include #include #include +#include struct console; -/* - * Type for character writing functions. - */ -typedef void (*console_putc_fn)(struct console *console, char c); +struct console_ops { + void (*putc)(struct console *console, char c); +}; +#define CONSOLE_BUF_SIZE 64 #define CONSOLE_NAME_SIZE 16 /* @@ -42,8 +44,11 @@ typedef void (*console_putc_fn)(struct console *console, char c); */ struct console { struct spinlock lock; + const struct console_ops *ops; + char buffer[CONSOLE_BUF_SIZE]; + struct cbuf recvbuf; + struct thread *waiter; struct list node; - console_putc_fn putc; char name[CONSOLE_NAME_SIZE]; }; @@ -51,7 +56,7 @@ struct console { * Console initialization. */ void console_init(struct console *console, const char *name, - console_putc_fn putc); + const struct console_ops *ops); /* * Initialize the console module. @@ -69,8 +74,22 @@ void console_setup(void); void console_register(struct console *console); /* - * Write a single character to all registered console devices. + * Console interrupt handler. + * + * This function is meant to be used by low-level drivers to fill the + * receive buffer. + * + * Interrupts must be disabled when calling this function. + */ +void console_intr(struct console *console, char c); + +/* + * Write/read a single character to all registered console devices. + * + * Writing may not block in order to allow printf functions to be used in any + * context. Reading may block waiting for input. */ -void console_write_char(char c); +void console_putchar(char c); +char console_getchar(void); #endif /* _KERN_CONSOLE_H */ diff --git a/kern/printf.c b/kern/printf.c index d7a1d10..f657fdf 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -54,7 +54,7 @@ vprintf(const char *format, va_list ap) length = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); for (ptr = printf_buffer; *ptr != '\0'; ptr++) { - console_write_char(*ptr); + console_putchar(*ptr); } spinlock_unlock_intr_restore(&printf_lock, flags); -- cgit v1.2.3 From 2f3437c5a936c9871644971952922e7040313854 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:28:29 +0200 Subject: x86/uart: implement input handling --- arch/x86/machine/uart.c | 150 +++++++++++++++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 52 deletions(-) diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 16b6d92..82b6c83 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -16,8 +16,6 @@ * * * TODO Make serial line parameters configurable. - * TODO 16550A support. - * TODO Interrupt probing and handling. */ #include @@ -25,6 +23,7 @@ #include #include +#include #include #include #include @@ -34,28 +33,37 @@ #define UART_BDA_COM1_OFFSET 0 -#define UART_REG_DAT 0 -#define UART_REG_DLL 0 -#define UART_REG_IER 1 -#define UART_REG_DLH 1 -#define UART_REG_LCR 3 -#define UART_REG_MCR 4 -#define UART_REG_LSR 5 -#define UART_NR_REGS 6 +#define UART_REG_DAT 0 +#define UART_REG_DLL 0 +#define UART_REG_IER 1 +#define UART_REG_DLH 1 +#define UART_REG_IIR 2 +#define UART_REG_LCR 3 +#define UART_REG_MCR 4 +#define UART_REG_LSR 5 +#define UART_REG_MSR 6 +#define UART_NR_REGS 7 -#define UART_THR_EMPTY 0x20 +#define UART_IER_RX 0x1 -#define UART_LCR_8BITS 0x03 -#define UART_LCR_1S 0x00 -#define UART_LCR_NP 0x00 -#define UART_LCR_BEN 0x40 -#define UART_LCR_DLAB 0x80 +#define UART_IIR_NOT_PENDING 0x1 +#define UART_IIR_SRC_RX 0x4 +#define UART_IIR_SRC_MASK 0xe -#define UART_MCR_DTR 0x01 -#define UART_MCR_RTS 0x02 -#define UART_MCR_AUX2 0x04 +#define UART_LCR_8BITS 0x03 +#define UART_LCR_1S 0x00 +#define UART_LCR_NP 0x00 +#define UART_LCR_BEN 0x40 +#define UART_LCR_DLAB 0x80 -#define UART_MAX_DEVS 4 +#define UART_MCR_DTR 0x01 +#define UART_MCR_RTS 0x02 +#define UART_MCR_AUX2 0x04 + +#define UART_LSR_DATA_READY 0x01 +#define UART_LSR_TX_EMPTY 0x20 + +#define UART_MAX_DEVS 4 struct uart { struct console console; @@ -121,30 +129,73 @@ uart_clear(struct uart *uart, uint16_t reg, uint8_t mask) } static void -uart_tx_wait(struct uart *uart) +uart_recv_intr(struct uart *uart) { uint8_t byte; for (;;) { byte = uart_read(uart, UART_REG_LSR); - if (byte & UART_THR_EMPTY) { + if (!(byte & UART_LSR_DATA_READY)) { break; } + + byte = uart_read(uart, UART_REG_DAT); + console_intr(&uart->console, (char)byte); } } -static struct uart * -uart_get_dev(size_t i) +static int +uart_intr(void *arg) { - assert(i < ARRAY_SIZE(uart_devs)); - return &uart_devs[i]; + struct uart *uart; + uint8_t byte; + + uart = arg; + + byte = uart_read(uart, UART_REG_IIR); + + if (byte & UART_IIR_NOT_PENDING) { + return ERROR_AGAIN; + } + + byte &= UART_IIR_SRC_MASK; + + if (byte == UART_IIR_SRC_RX) { + uart_recv_intr(uart); + } + + return 0; } -static struct uart * -uart_get_from_console(struct console *console) +static void __init +uart_enable_intr(struct uart *uart) { - return structof(console, struct uart, console); + int error; + + error = intr_register(uart->intr, uart_intr, uart); + + if (error) { + printf("uart%zu: error: unable to register interrupt %u\n", + uart_get_id(uart), uart->intr); + return; + } + + uart_write(uart, UART_REG_IER, UART_IER_RX); +} + +static void +uart_tx_wait(struct uart *uart) +{ + uint8_t byte; + + for (;;) { + byte = uart_read(uart, UART_REG_LSR); + + if (byte & UART_LSR_TX_EMPTY) { + break; + } + } } static void @@ -164,13 +215,26 @@ uart_write_char(struct uart *uart, char c) uart_write_char_common(uart, c); } +static struct uart * +uart_get_dev(size_t i) +{ + assert(i < ARRAY_SIZE(uart_devs)); + return &uart_devs[i]; +} + +static struct uart * +uart_get_from_console(struct console *console) +{ + return structof(console, struct uart, console); +} + static void uart_console_putc(struct console *console, char c) { uart_write_char(uart_get_from_console(console), c); } -static struct console_ops uart_console_ops = { +static const struct console_ops uart_console_ops = { .putc = uart_console_putc, }; @@ -184,14 +248,15 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) uart->intr = intr; uart_write(uart, UART_REG_IER, 0); - uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR); uart_set(uart, UART_REG_LCR, UART_LCR_DLAB); uart_write(uart, UART_REG_DLH, 0); uart_write(uart, UART_REG_DLL, 1); uart_clear(uart, UART_REG_LCR, UART_LCR_DLAB); - byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S | UART_LCR_BEN; + uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR); + + byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S; uart_write(uart, UART_REG_LCR, byte); snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); @@ -216,23 +281,10 @@ uart_bootstrap(void) } } -static int -uart_intr(void *arg) -{ - struct uart *uart; - uint8_t byte; - - uart = arg; - byte = uart_read(uart, UART_REG_DAT); - printf("uart: intr:%u byte:%hhu (%c)\n", uart->intr, byte, byte); - return 0; -} - void __init uart_setup(void) { struct uart *uart; - int error; size_t i; for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { @@ -242,13 +294,7 @@ uart_setup(void) continue; } - error = intr_register(uart->intr, uart_intr, uart); - - if (error) { - printf("uart%zu: unable to register interrupt %u\n", i, uart->intr); - } - - uart_write(uart, UART_REG_IER, 1); + uart_enable_intr(uart); } } -- cgit v1.2.3 From 5a711e07fe97d3fdbd6e322bacd41de44bdf82d3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:33:26 +0200 Subject: kern/error: new ERROR_EXIST and ERROR_IO macros --- kern/error.c | 4 ++++ kern/error.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/kern/error.c b/kern/error.c index e37c67c..02a26d0 100644 --- a/kern/error.c +++ b/kern/error.c @@ -36,6 +36,10 @@ error_str(int error) return "Bad address"; case ERROR_NODEV: return "No such device"; + case ERROR_EXIST: + return "Entry exists"; + case ERROR_IO: + return "Input/output error"; default: return "unknown error"; } diff --git a/kern/error.h b/kern/error.h index 62859f4..96a2620 100644 --- a/kern/error.h +++ b/kern/error.h @@ -24,6 +24,8 @@ #define ERROR_BUSY 4 #define ERROR_FAULT 5 #define ERROR_NODEV 6 +#define ERROR_EXIST 7 +#define ERROR_IO 8 /* * Return a string describing the given error. -- cgit v1.2.3 From 001ea7c0bbe1b5fb440b5e628faa47d8a58f36b7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:31:04 +0200 Subject: x86: new atcons and atkbd modules These modules, together with cga, implement the AT console driver. --- arch/x86/Makefrag.am | 4 + arch/x86/machine/atcons.c | 56 ++++ arch/x86/machine/atcons.h | 44 +++ arch/x86/machine/atkbd.c | 811 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/atkbd.h | 29 ++ arch/x86/machine/boot.c | 4 +- arch/x86/machine/cga.c | 23 +- arch/x86/machine/cga.h | 5 + 8 files changed, 955 insertions(+), 21 deletions(-) create mode 100644 arch/x86/machine/atcons.c create mode 100644 arch/x86/machine/atcons.h create mode 100644 arch/x86/machine/atkbd.c create mode 100644 arch/x86/machine/atkbd.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 6adc769..0971530 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -26,6 +26,10 @@ endif AMD64 x15_SOURCES += \ arch/x86/machine/acpimp.c \ arch/x86/machine/acpimp.h \ + arch/x86/machine/atcons.c \ + arch/x86/machine/atcons.h \ + arch/x86/machine/atkbd.c \ + arch/x86/machine/atkbd.h \ arch/x86/machine/asm.h \ arch/x86/machine/atomic.h \ arch/x86/machine/biosmem.c \ diff --git a/arch/x86/machine/atcons.c b/arch/x86/machine/atcons.c new file mode 100644 index 0000000..c4c4806 --- /dev/null +++ b/arch/x86/machine/atcons.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 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 3 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 + +static struct console atcons_console; + +static void +atcons_putc(struct console *console, char c) +{ + (void)console; + cga_putc(c); +} + +static const struct console_ops atcons_ops = { + .putc = atcons_putc, +}; + +void __init +atcons_bootstrap(void) +{ + cga_setup(); + + console_init(&atcons_console, "atcons", &atcons_ops); + console_register(&atcons_console); +} + +void __init +atcons_setup(void) +{ + atkbd_setup(); +} + +void +atcons_intr(char c) +{ + console_intr(&atcons_console, c); +} diff --git a/arch/x86/machine/atcons.h b/arch/x86/machine/atcons.h new file mode 100644 index 0000000..05b870f --- /dev/null +++ b/arch/x86/machine/atcons.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * AT console driver. + */ + +#ifndef _X86_ATCONS_H +#define _X86_ATCONS_H + +/* + * Early initialization of the atcons module. + */ +void atcons_bootstrap(void); + +/* + * Initialize the atcons module. + * + * This function enables keyboard interrupt handling. + */ +void atcons_setup(void); + +/* + * Console interrupt handler. + * + * This function is called by the AT keyboard interrupt handler + * to handle machine-independent console management. + */ +void atcons_intr(char c); + +#endif /* _X86_ATCONS_H */ diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c new file mode 100644 index 0000000..362b834 --- /dev/null +++ b/arch/x86/machine/atkbd.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2017 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 3 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 + +#define ATKBD_PORT_DATA 0x60 +#define ATKBD_PORT_STATUS 0x64 +#define ATKBD_PORT_CMD 0x64 + +#define ATKBD_REQ_LEDS 0xed +#define ATKBD_REQ_RESET 0xff + +#define ATKBD_REP_PASS 0xaa +#define ATKBD_REP_ACK 0xfa +#define ATKBD_REP_RESEND 0xfe + +#define ATKBD_LEDS_SCROLLLOCK 0x01 +#define ATKBD_LEDS_NUMLOCK 0x02 +#define ATKBD_LEDS_CAPSLOCK 0x04 + +#define ATKBD_STATUS_OUT_FULL 0x01 +#define ATKBD_STATUS_IN_FULL 0x02 +#define ATKBD_STATUS_PARITY_ERROR 0x80 + +#define ATKBD_CMD_RDCONF 0x20 +#define ATKBD_CMD_WRCONF 0x60 +#define ATKBD_CMD_DIS2 0xa7 +#define ATKBD_CMD_EN2 0xa8 +#define ATKBD_CMD_DIS1 0xad +#define ATKBD_CMD_EN1 0xae + +#define ATKBD_CONF_ENINT1 0x01 +#define ATKBD_CONF_ENINT2 0x02 +#define ATKBD_CONF_ENTRANS 0x40 + +#define ATKBD_INTR1 1 + +enum atkbd_key_id { + ATKBD_KEY_INVALID = 0, + ATKBD_KEY_1 = 1, + ATKBD_KEY_2, + ATKBD_KEY_3, + ATKBD_KEY_4, + ATKBD_KEY_5, + ATKBD_KEY_6, + ATKBD_KEY_7, + ATKBD_KEY_8, + ATKBD_KEY_9, + ATKBD_KEY_0, + ATKBD_KEY_DASH, + ATKBD_KEY_EQUAL, + ATKBD_KEY_BACKSLASH, + ATKBD_KEY_BACKSPACE, + + ATKBD_KEY_TAB, + ATKBD_KEY_Q, + ATKBD_KEY_W, + ATKBD_KEY_E, + ATKBD_KEY_R, + ATKBD_KEY_T, + ATKBD_KEY_Y, + ATKBD_KEY_U, + ATKBD_KEY_I, + ATKBD_KEY_O, + ATKBD_KEY_P, + ATKBD_KEY_OBRACKET, + ATKBD_KEY_CBRACKET, + ATKBD_KEY_ENTER, + + ATKBD_KEY_CAPSLOCK, + ATKBD_KEY_A, + ATKBD_KEY_S, + ATKBD_KEY_D, + ATKBD_KEY_F, + ATKBD_KEY_G, + ATKBD_KEY_H, + ATKBD_KEY_J, + ATKBD_KEY_K, + ATKBD_KEY_L, + ATKBD_KEY_SEMICOLON, + ATKBD_KEY_QUOTE, + + ATKBD_KEY_LSHIFT, + ATKBD_KEY_Z, + ATKBD_KEY_X, + ATKBD_KEY_C, + ATKBD_KEY_V, + ATKBD_KEY_B, + ATKBD_KEY_N, + ATKBD_KEY_M, + ATKBD_KEY_COMMA, + ATKBD_KEY_DOT, + ATKBD_KEY_SLASH, + ATKBD_KEY_RSHIFT, + + ATKBD_KEY_LCTRL, + ATKBD_KEY_ALT, + ATKBD_KEY_SPACE, + ATKBD_KEY_ALTGR, + ATKBD_KEY_RCTRL, + + ATKBD_KEY_INSERT, + ATKBD_KEY_DELETE, + ATKBD_KEY_HOME, + ATKBD_KEY_END, + ATKBD_KEY_PGUP, + ATKBD_KEY_PGDOWN, + + ATKBD_KEY_LEFT, + ATKBD_KEY_BOTTOM, + ATKBD_KEY_RIGHT, + ATKBD_KEY_UP, + + ATKBD_KEY_KP_NUMLOCK, + ATKBD_KEY_KP_SLASH, + ATKBD_KEY_KP_STAR, + ATKBD_KEY_KP_MINUS, + ATKBD_KEY_KP_HOME, + ATKBD_KEY_KP_UP, + ATKBD_KEY_KP_PGUP, + ATKBD_KEY_KP_PLUS, + ATKBD_KEY_KP_LEFT, + ATKBD_KEY_KP_5, + ATKBD_KEY_KP_RIGHT, + ATKBD_KEY_KP_END, + ATKBD_KEY_KP_BOTTOM, + ATKBD_KEY_KP_PGDOWN, + ATKBD_KEY_KP_ENTER, + ATKBD_KEY_KP_INS, + ATKBD_KEY_KP_DEL, +}; + +#define ATKBD_KM_SHIFT 0x01 /* Shift / caps lock modifier applies */ +#define ATKBD_KM_KP 0x02 /* Num lock modifier applies */ +#define ATKBD_KM_CTL 0x04 /* Unmodified key is a control character */ + +struct atkbd_key { + int modifiers; + enum atkbd_key_id id; +}; + +static const struct atkbd_key atkbd_keys[] = { + [0x16] = { ATKBD_KM_SHIFT, ATKBD_KEY_1 }, + [0x1e] = { ATKBD_KM_SHIFT, ATKBD_KEY_2 }, + [0x26] = { ATKBD_KM_SHIFT, ATKBD_KEY_3 }, + [0x25] = { ATKBD_KM_SHIFT, ATKBD_KEY_4 }, + [0x2e] = { ATKBD_KM_SHIFT, ATKBD_KEY_5 }, + [0x36] = { ATKBD_KM_SHIFT, ATKBD_KEY_6 }, + [0x3d] = { ATKBD_KM_SHIFT, ATKBD_KEY_7 }, + [0x3e] = { ATKBD_KM_SHIFT, ATKBD_KEY_8 }, + [0x46] = { ATKBD_KM_SHIFT, ATKBD_KEY_9 }, + [0x45] = { ATKBD_KM_SHIFT, ATKBD_KEY_0 }, + [0x4e] = { ATKBD_KM_SHIFT, ATKBD_KEY_DASH }, + [0x55] = { ATKBD_KM_SHIFT, ATKBD_KEY_EQUAL }, + [0x5d] = { ATKBD_KM_SHIFT, ATKBD_KEY_BACKSLASH }, + [0x66] = { 0, ATKBD_KEY_BACKSPACE }, + + [0x0d] = { 0, ATKBD_KEY_TAB }, + [0x15] = { ATKBD_KM_SHIFT, ATKBD_KEY_Q }, + [0x1d] = { ATKBD_KM_SHIFT, ATKBD_KEY_W }, + [0x24] = { ATKBD_KM_SHIFT, ATKBD_KEY_E }, + [0x2d] = { ATKBD_KM_SHIFT, ATKBD_KEY_R }, + [0x2c] = { ATKBD_KM_SHIFT, ATKBD_KEY_T }, + [0x35] = { ATKBD_KM_SHIFT, ATKBD_KEY_Y }, + [0x3c] = { ATKBD_KM_SHIFT, ATKBD_KEY_U }, + [0x43] = { ATKBD_KM_SHIFT, ATKBD_KEY_I }, + [0x44] = { ATKBD_KM_SHIFT, ATKBD_KEY_O }, + [0x4d] = { ATKBD_KM_SHIFT, ATKBD_KEY_P }, + [0x54] = { ATKBD_KM_SHIFT, ATKBD_KEY_OBRACKET }, + [0x5b] = { ATKBD_KM_SHIFT, ATKBD_KEY_CBRACKET }, + [0x5a] = { 0, ATKBD_KEY_ENTER }, + + [0x58] = { ATKBD_KM_CTL, ATKBD_KEY_CAPSLOCK }, + [0x1c] = { ATKBD_KM_SHIFT, ATKBD_KEY_A }, + [0x1b] = { ATKBD_KM_SHIFT, ATKBD_KEY_S }, + [0x23] = { ATKBD_KM_SHIFT, ATKBD_KEY_D }, + [0x2b] = { ATKBD_KM_SHIFT, ATKBD_KEY_F }, + [0x34] = { ATKBD_KM_SHIFT, ATKBD_KEY_G }, + [0x33] = { ATKBD_KM_SHIFT, ATKBD_KEY_H }, + [0x3b] = { ATKBD_KM_SHIFT, ATKBD_KEY_J }, + [0x42] = { ATKBD_KM_SHIFT, ATKBD_KEY_K }, + [0x4b] = { ATKBD_KM_SHIFT, ATKBD_KEY_L }, + [0x4c] = { ATKBD_KM_SHIFT, ATKBD_KEY_SEMICOLON }, + [0x52] = { ATKBD_KM_SHIFT, ATKBD_KEY_QUOTE }, + + [0x12] = { ATKBD_KM_CTL, ATKBD_KEY_LSHIFT }, + [0x1a] = { ATKBD_KM_SHIFT, ATKBD_KEY_Z }, + [0x22] = { ATKBD_KM_SHIFT, ATKBD_KEY_X }, + [0x21] = { ATKBD_KM_SHIFT, ATKBD_KEY_C }, + [0x2a] = { ATKBD_KM_SHIFT, ATKBD_KEY_V }, + [0x32] = { ATKBD_KM_SHIFT, ATKBD_KEY_B }, + [0x31] = { ATKBD_KM_SHIFT, ATKBD_KEY_N }, + [0x3a] = { ATKBD_KM_SHIFT, ATKBD_KEY_M }, + [0x41] = { ATKBD_KM_SHIFT, ATKBD_KEY_COMMA }, + [0x49] = { ATKBD_KM_SHIFT, ATKBD_KEY_DOT }, + [0x4a] = { ATKBD_KM_SHIFT, ATKBD_KEY_SLASH }, + [0x59] = { ATKBD_KM_CTL, ATKBD_KEY_RSHIFT }, + + [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_LCTRL }, + [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALT }, + [0x29] = { 0, ATKBD_KEY_SPACE }, + + [0x77] = { ATKBD_KM_CTL, ATKBD_KEY_KP_NUMLOCK }, + [0x7c] = { 0, ATKBD_KEY_KP_STAR }, + [0x7b] = { 0, ATKBD_KEY_KP_MINUS }, + [0x6c] = { ATKBD_KM_KP, ATKBD_KEY_KP_HOME }, + [0x75] = { ATKBD_KM_KP, ATKBD_KEY_KP_UP }, + [0x7d] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGUP }, + [0x79] = { 0, ATKBD_KEY_KP_PLUS }, + [0x6b] = { ATKBD_KM_KP, ATKBD_KEY_KP_LEFT }, + [0x73] = { 0, ATKBD_KEY_KP_5 }, + [0x74] = { ATKBD_KM_KP, ATKBD_KEY_KP_RIGHT }, + [0x69] = { ATKBD_KM_KP, ATKBD_KEY_KP_END }, + [0x72] = { ATKBD_KM_KP, ATKBD_KEY_KP_BOTTOM }, + [0x7a] = { ATKBD_KM_KP, ATKBD_KEY_KP_PGDOWN }, + [0x70] = { ATKBD_KM_CTL | ATKBD_KM_KP, ATKBD_KEY_KP_INS }, + [0x71] = { ATKBD_KM_KP, ATKBD_KEY_KP_DEL }, +}; + +static const struct atkbd_key atkbd_e0_keys[] = { + [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALTGR }, + [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_RCTRL }, + + [0x70] = { ATKBD_KM_CTL, ATKBD_KEY_INSERT }, + [0x71] = { 0, ATKBD_KEY_DELETE }, + [0x6c] = { 0, ATKBD_KEY_HOME }, + [0x69] = { 0, ATKBD_KEY_END }, + [0x7d] = { 0, ATKBD_KEY_PGUP }, + [0x7a] = { 0, ATKBD_KEY_PGDOWN }, + + [0x6b] = { 0, ATKBD_KEY_LEFT }, + [0x72] = { 0, ATKBD_KEY_BOTTOM }, + [0x74] = { 0, ATKBD_KEY_RIGHT }, + [0x75] = { 0, ATKBD_KEY_UP }, + + [0x4a] = { 0, ATKBD_KEY_KP_SLASH }, + [0x5a] = { 0, ATKBD_KEY_KP_ENTER }, +}; + +static const char *atkbd_chars[] = { + [ATKBD_KEY_1] = "1", + [ATKBD_KEY_2] = "2", + [ATKBD_KEY_3] = "3", + [ATKBD_KEY_4] = "4", + [ATKBD_KEY_5] = "5", + [ATKBD_KEY_6] = "6", + [ATKBD_KEY_7] = "7", + [ATKBD_KEY_8] = "8", + [ATKBD_KEY_9] = "9", + [ATKBD_KEY_0] = "0", + [ATKBD_KEY_DASH] = "-", + [ATKBD_KEY_EQUAL] = "=", + [ATKBD_KEY_BACKSLASH] = "\\", + [ATKBD_KEY_BACKSPACE] = "\b", + + [ATKBD_KEY_TAB] = "\t", + [ATKBD_KEY_Q] = "q", + [ATKBD_KEY_W] = "w", + [ATKBD_KEY_E] = "e", + [ATKBD_KEY_R] = "r", + [ATKBD_KEY_T] = "t", + [ATKBD_KEY_Y] = "y", + [ATKBD_KEY_U] = "u", + [ATKBD_KEY_I] = "i", + [ATKBD_KEY_O] = "o", + [ATKBD_KEY_P] = "p", + [ATKBD_KEY_OBRACKET] = "[", + [ATKBD_KEY_CBRACKET] = "]", + [ATKBD_KEY_ENTER] = "\n", + + [ATKBD_KEY_A] = "a", + [ATKBD_KEY_S] = "s", + [ATKBD_KEY_D] = "d", + [ATKBD_KEY_F] = "f", + [ATKBD_KEY_G] = "g", + [ATKBD_KEY_H] = "h", + [ATKBD_KEY_J] = "j", + [ATKBD_KEY_K] = "k", + [ATKBD_KEY_L] = "l", + [ATKBD_KEY_SEMICOLON] = ";", + [ATKBD_KEY_QUOTE] = "'", + + [ATKBD_KEY_Z] = "z", + [ATKBD_KEY_X] = "x", + [ATKBD_KEY_C] = "c", + [ATKBD_KEY_V] = "v", + [ATKBD_KEY_B] = "b", + [ATKBD_KEY_N] = "n", + [ATKBD_KEY_M] = "m", + [ATKBD_KEY_COMMA] = ",", + [ATKBD_KEY_DOT] = ".", + [ATKBD_KEY_SLASH] = "/", + + [ATKBD_KEY_SPACE] = " ", + + [ATKBD_KEY_DELETE] = "\e[3~", + + [ATKBD_KEY_LEFT] = "\e[D", + [ATKBD_KEY_BOTTOM] = "\e[B", + [ATKBD_KEY_RIGHT] = "\e[C", + [ATKBD_KEY_UP] = "\e[A", + + [ATKBD_KEY_KP_SLASH] = "/", + [ATKBD_KEY_KP_STAR] = "*", + [ATKBD_KEY_KP_MINUS] = "-", + [ATKBD_KEY_KP_PLUS] = "+", + [ATKBD_KEY_KP_5] = "5", + [ATKBD_KEY_KP_ENTER] = "\n", + [ATKBD_KEY_KP_DEL] = "\e[3~", +}; + +static const char *atkbd_shift_chars[] = { + [ATKBD_KEY_1] = "!", + [ATKBD_KEY_2] = "@", + [ATKBD_KEY_3] = "#", + [ATKBD_KEY_4] = "$", + [ATKBD_KEY_5] = "%", + [ATKBD_KEY_6] = "^", + [ATKBD_KEY_7] = "&", + [ATKBD_KEY_8] = "*", + [ATKBD_KEY_9] = "(", + [ATKBD_KEY_0] = ")", + [ATKBD_KEY_DASH] = "_", + [ATKBD_KEY_EQUAL] = "+", + [ATKBD_KEY_BACKSLASH] = "|", + + [ATKBD_KEY_Q] = "Q", + [ATKBD_KEY_W] = "W", + [ATKBD_KEY_E] = "E", + [ATKBD_KEY_R] = "R", + [ATKBD_KEY_T] = "T", + [ATKBD_KEY_Y] = "Y", + [ATKBD_KEY_U] = "U", + [ATKBD_KEY_I] = "I", + [ATKBD_KEY_O] = "O", + [ATKBD_KEY_P] = "P", + [ATKBD_KEY_OBRACKET] = "{", + [ATKBD_KEY_CBRACKET] = "}", + + [ATKBD_KEY_A] = "A", + [ATKBD_KEY_S] = "S", + [ATKBD_KEY_D] = "D", + [ATKBD_KEY_F] = "F", + [ATKBD_KEY_G] = "G", + [ATKBD_KEY_H] = "H", + [ATKBD_KEY_J] = "J", + [ATKBD_KEY_K] = "K", + [ATKBD_KEY_L] = "L", + [ATKBD_KEY_SEMICOLON] = ":", + [ATKBD_KEY_QUOTE] = "\"", + + [ATKBD_KEY_Z] = "Z", + [ATKBD_KEY_X] = "X", + [ATKBD_KEY_C] = "C", + [ATKBD_KEY_V] = "V", + [ATKBD_KEY_B] = "B", + [ATKBD_KEY_N] = "N", + [ATKBD_KEY_M] = "M", + [ATKBD_KEY_COMMA] = "<", + [ATKBD_KEY_DOT] = ">", + [ATKBD_KEY_SLASH] = "?", +}; + +static const char *atkbd_kp_chars[] = { + [ATKBD_KEY_KP_HOME] = "7", + [ATKBD_KEY_KP_UP] = "8", + [ATKBD_KEY_KP_PGUP] = "9", + [ATKBD_KEY_KP_LEFT] = "4", + [ATKBD_KEY_KP_RIGHT] = "6", + [ATKBD_KEY_KP_END] = "1", + [ATKBD_KEY_KP_BOTTOM] = "2", + [ATKBD_KEY_KP_PGDOWN] = "3", + [ATKBD_KEY_KP_INS] = "0", + [ATKBD_KEY_KP_DEL] = ".", +}; + +#define ATKBD_KF_E0 0x01 +#define ATKBD_KF_F0 0x02 +#define ATKBD_KF_LSHIFT 0x04 +#define ATKBD_KF_RSHIFT 0x08 +#define ATKBD_KF_NUMLOCK 0x10 +#define ATKBD_KF_CAPSLOCK 0x20 +#define ATKBD_KF_SCROLLLOCK 0x40 + +#define ATKBD_KF_SHIFT (ATKBD_KF_CAPSLOCK \ + | ATKBD_KF_RSHIFT \ + | ATKBD_KF_LSHIFT) + +/* + * These flags are only accessed during interrupt handling and don't + * require additional synchronization. + */ +static unsigned int atkbd_flags; + +static uint8_t +atkbd_read_data(void) +{ + return io_read_byte(ATKBD_PORT_DATA); +} + +static void +atkbd_write_data(uint8_t data) +{ + io_write_byte(ATKBD_PORT_DATA, data); +} + +static uint8_t +atkbd_read_status(void) +{ + return io_read_byte(ATKBD_PORT_STATUS); +} + +static void +atkbd_write_cmd(uint8_t cmd) +{ + io_write_byte(ATKBD_PORT_CMD, cmd); +} + +static int +atkbd_out_wait(void) +{ + uint8_t status; + + for (;;) { + status = atkbd_read_status(); + + if (status & ATKBD_STATUS_OUT_FULL) { + break; + } + } + + if (status & ATKBD_STATUS_PARITY_ERROR) { + printf("atkbd: parity error\n"); + return ERROR_IO; + } + + return 0; +} + +static void +atkbd_in_wait(void) +{ + uint8_t status; + + for (;;) { + status = atkbd_read_status(); + + if (!(status & ATKBD_STATUS_IN_FULL)) { + break; + } + } +} + +static int +atkbd_read(uint8_t *datap, bool wait) +{ + uint8_t status; + int error; + + if (wait) { + error = atkbd_out_wait(); + + if (error) { + return error; + } + } else { + status = atkbd_read_status(); + + if (!(status & ATKBD_STATUS_OUT_FULL)) { + return ERROR_AGAIN; + } + } + + *datap = atkbd_read_data(); + return 0; +} + +static void +atkbd_write(uint8_t data) +{ + atkbd_in_wait(); + atkbd_write_data(data); +} + +static void +atkbd_flush(void) +{ + uint8_t status; + + atkbd_read(&status, false); +} + +static void __init +atkbd_reset_kbd(void) +{ + uint8_t reply; + int error; + +resend: + atkbd_write(ATKBD_REQ_RESET); + error = atkbd_read(&reply, true); + + if (error) { + return; + } + + if (reply == ATKBD_REP_RESEND) { + goto resend; + } + + if ((reply != ATKBD_REP_PASS) && (reply != ATKBD_REP_ACK)) { + printf("atkbd: warning: unable to reset keyboard\n"); + } +} + +static int __init +atkbd_disable(void) +{ + uint8_t byte; + int error; + + atkbd_write_cmd(ATKBD_CMD_DIS1); + atkbd_write_cmd(ATKBD_CMD_DIS2); + + atkbd_flush(); + + atkbd_write_cmd(ATKBD_CMD_RDCONF); + error = atkbd_read(&byte, true); + + if (error) { + return error; + } + + byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2 | ATKBD_CONF_ENINT1); + atkbd_write_cmd(ATKBD_CMD_WRCONF); + atkbd_write(byte); + + return 0; +} + +static int __init +atkbd_enable(void) +{ + uint8_t byte; + int error; + + atkbd_write_cmd(ATKBD_CMD_EN1); + + atkbd_write_cmd(ATKBD_CMD_RDCONF); + error = atkbd_read(&byte, true); + + if (error) { + return error; + } + + byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2); + byte |= ATKBD_CONF_ENINT1; + atkbd_write_cmd(ATKBD_CMD_WRCONF); + atkbd_write(byte); + + atkbd_flush(); + + return 0; +} + +static void +atkbd_set_leds(void) +{ + uint8_t leds, reply; + int error; + + leds = ((atkbd_flags & ATKBD_KF_NUMLOCK) ? ATKBD_LEDS_NUMLOCK : 0) + | ((atkbd_flags & ATKBD_KF_CAPSLOCK) ? ATKBD_LEDS_CAPSLOCK : 0) + | ((atkbd_flags & ATKBD_KF_SCROLLLOCK) ? ATKBD_LEDS_SCROLLLOCK : 0); + +resend: + atkbd_write(ATKBD_REQ_LEDS); + atkbd_write(leds); + error = atkbd_read(&reply, true); + + if (error) { + return; + } + + if (reply == ATKBD_REP_RESEND) { + goto resend; + } + + if (reply != ATKBD_REP_ACK) { + printf("atkbd: warning: invalid reply from keyboard\n"); + } +} + +static void +atkbd_toggle_numlock(void) +{ + atkbd_flags ^= ATKBD_KF_NUMLOCK; + atkbd_set_leds(); +} + +static void +atkbd_toggle_capslock(void) +{ + atkbd_flags ^= ATKBD_KF_CAPSLOCK; + atkbd_set_leds(); +} + +static void +atkbd_key_process_chars(const struct atkbd_key *key, + const char **chars, size_t size) +{ + const char *s; + + if (key->id >= size) { + return; + } + + if (atkbd_flags & ATKBD_KF_F0) { + return; + } + + s = chars[key->id]; + + if (s != NULL) { + while (*s != '\0') { + atcons_intr(*s); + s++; + } + } +} + +static void +atkbd_key_process_shift(const struct atkbd_key *key) +{ + atkbd_key_process_chars(key, atkbd_shift_chars, + ARRAY_SIZE(atkbd_shift_chars)); +} + +static void +atkbd_key_process_kp(const struct atkbd_key *key) +{ + atkbd_key_process_chars(key, atkbd_kp_chars, + ARRAY_SIZE(atkbd_kp_chars)); +} + +static void +atkbd_key_process_ctl(const struct atkbd_key *key) +{ + switch (key->id) { + case ATKBD_KEY_LSHIFT: + if (atkbd_flags & ATKBD_KF_F0) { + atkbd_flags &= ~ATKBD_KF_LSHIFT; + } else { + atkbd_flags |= ATKBD_KF_LSHIFT; + } + + break; + case ATKBD_KEY_RSHIFT: + if (atkbd_flags & ATKBD_KF_F0) { + atkbd_flags &= ~ATKBD_KF_RSHIFT; + } else { + atkbd_flags |= ATKBD_KF_RSHIFT; + } + + break; + case ATKBD_KEY_KP_NUMLOCK: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atkbd_toggle_numlock(); + } + + break; + case ATKBD_KEY_CAPSLOCK: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atkbd_toggle_capslock(); + } + default: + break; + } +} + +static void +atkbd_key_process(const struct atkbd_key *key) +{ + if (key->id == ATKBD_KEY_INVALID) { + return; + } + + if ((key->modifiers & ATKBD_KM_SHIFT) && (atkbd_flags & ATKBD_KF_SHIFT)) { + atkbd_key_process_shift(key); + } else if ((key->modifiers & ATKBD_KM_KP) + && (atkbd_flags & ATKBD_KF_NUMLOCK)) { + atkbd_key_process_kp(key); + } else if (key->modifiers & ATKBD_KM_CTL) { + atkbd_key_process_ctl(key); + } else { + atkbd_key_process_chars(key, atkbd_chars, ARRAY_SIZE(atkbd_chars)); + } + + atkbd_flags &= ~ATKBD_KF_F0; +} + +static void +atkbd_process_e0_code(uint8_t code) +{ + if (code == 0xf0) { + atkbd_flags |= ATKBD_KF_F0; + return; + } + + if (code >= ARRAY_SIZE(atkbd_keys)) { + return; + } + + atkbd_key_process(&atkbd_e0_keys[code]); + atkbd_flags &= ~ATKBD_KF_E0; +} + +static void +atkbd_process_code(uint8_t code) +{ + if (code == 0xe0) { + atkbd_flags |= ATKBD_KF_E0; + return; + } else if (code == 0xf0) { + atkbd_flags |= ATKBD_KF_F0; + return; + } + + if (code >= ARRAY_SIZE(atkbd_keys)) { + return; + } + + atkbd_key_process(&atkbd_keys[code]); +} + +static int +atkbd_intr(void *arg) +{ + uint8_t code; + int error; + + (void)arg; + + for (;;) { + error = atkbd_read(&code, false); + + if (error) { + return 0; + } + + if (atkbd_flags & ATKBD_KF_E0) { + atkbd_process_e0_code(code); + } else { + atkbd_process_code(code); + } + } + + return 0; +} + +void __init +atkbd_setup(void) +{ + int error; + + error = atkbd_disable(); + + if (error) { + return; + } + + error = intr_register(ATKBD_INTR1, atkbd_intr, NULL); + + if (error) { + printf("atkbd: error: unable to register interrupt handler\n"); + return; + } + + error = atkbd_enable(); + + if (error) { + return; + } + + atkbd_reset_kbd(); +} diff --git a/arch/x86/machine/atkbd.h b/arch/x86/machine/atkbd.h new file mode 100644 index 0000000..0bed7ad --- /dev/null +++ b/arch/x86/machine/atkbd.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Tiny AT keyboard driver. + */ + +#ifndef _X86_ATKBD_H +#define _X86_ATKBD_H + +/* + * Initialize the atkbd module. + */ +void atkbd_setup(void); + +#endif /* _X86_ATKBD_H */ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 0df9674..b7a9b0e 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -468,7 +469,7 @@ boot_main(void) cpu_setup(); thread_bootstrap(); console_setup(); - cga_setup(); + atcons_bootstrap(); uart_bootstrap(); printf_setup(); boot_show_version(); @@ -485,6 +486,7 @@ boot_main(void) intr_setup(); cpu_mp_probe(); pic_setup(); + atcons_setup(); uart_setup(); kernel_main(); diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 3f1525c..1ec8035 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -70,8 +69,6 @@ static uint8_t *cga_memory __read_mostly; static uint16_t cga_cursor; -static struct console cga_console; - static uint16_t cga_get_cursor_position(void) { @@ -119,8 +116,8 @@ cga_scroll_lines(void) } } -static void -cga_write_char(char c) +void +cga_putc(char c) { if (c == '\r') { return; @@ -143,7 +140,7 @@ cga_write_char(char c) int i; for(i = 0; i < CGA_TABULATION_SPACES; i++) { - cga_write_char(' '); + cga_putc(' '); } } else { if ((cga_cursor + 1) >= CGA_COLUMNS * CGA_LINES) { @@ -158,17 +155,6 @@ cga_write_char(char c) } } -static void -cga_console_putc(struct console *console, char c) -{ - (void)console; - cga_write_char(c); -} - -static const struct console_ops cga_console_ops = { - .putc = cga_console_putc, -}; - void __init cga_setup(void) { @@ -194,7 +180,4 @@ cga_setup(void) } cga_cursor = cga_get_cursor_position(); - - console_init(&cga_console, "cga", &cga_console_ops); - console_register(&cga_console); } diff --git a/arch/x86/machine/cga.h b/arch/x86/machine/cga.h index a677162..f5a5d28 100644 --- a/arch/x86/machine/cga.h +++ b/arch/x86/machine/cga.h @@ -26,4 +26,9 @@ */ void cga_setup(void); +/* + * Append a character to the CGA screen. + */ +void cga_putc(char c); + #endif /* _X86_CGA_H */ -- cgit v1.2.3 From 90fd42c78fc54ec81a8979d8cbcc12350cf2ec01 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:37:48 +0200 Subject: kern/intr: gracefully handle spurious interrupts --- kern/intr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kern/intr.c b/kern/intr.c index 0d3a4a8..ecc2edf 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -411,6 +411,11 @@ intr_handle(unsigned int intr) spinlock_lock(&entry->lock); + if (intr_entry_empty(entry)) { + printf("intr: spurious interrupt %u\n", intr); + goto out; + } + intr_entry_eoi(entry, intr); list_for_each_entry(&entry->handlers, handler, node) { @@ -421,5 +426,6 @@ intr_handle(unsigned int intr) } } +out: spinlock_unlock(&entry->lock); } -- cgit v1.2.3 From 955ceba97a07d11305404aaf62dd052a20d6414f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:38:35 +0200 Subject: kern/intr: send EOI after interrupt handling This seems to be required for the AT keyboard. --- kern/intr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kern/intr.c b/kern/intr.c index ecc2edf..2bbfb7c 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -416,8 +416,6 @@ intr_handle(unsigned int intr) goto out; } - intr_entry_eoi(entry, intr); - list_for_each_entry(&entry->handlers, handler, node) { error = intr_handler_run(handler); @@ -426,6 +424,8 @@ intr_handle(unsigned int intr) } } + intr_entry_eoi(entry, intr); + out: spinlock_unlock(&entry->lock); } -- cgit v1.2.3 From 4631ace136b0e8d2a34ac5399857540d8ab08d93 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:46:02 +0200 Subject: kern/string: implement strncmp --- kern/string.c | 23 +++++++++++++++++++++++ kern/string.h | 1 + 2 files changed, 24 insertions(+) diff --git a/kern/string.c b/kern/string.c index b77aa2f..584030b 100644 --- a/kern/string.c +++ b/kern/string.c @@ -177,6 +177,29 @@ strcmp(const char *s1, const char *s2) } #endif /* ARCH_STRING_STRCMP */ +#ifndef ARCH_STRING_STRNCMP +int +strncmp(const char *s1, const char *s2, size_t n) +{ + char c1, c2; + + c1 = '\0'; + c2 = '\0'; + + while ((n != 0) && (c1 = *s1) == (c2 = *s2)) { + if (c1 == '\0') { + return 0; + } + + n--; + s1++; + s2++; + } + + return (int)c1 - (int)c2; +} +#endif /* ARCH_STRING_STRNCMP */ + #ifndef ARCH_STRING_STRCHR char * strchr(const char *s, int c) diff --git a/kern/string.h b/kern/string.h index 86786c2..35490ff 100644 --- a/kern/string.h +++ b/kern/string.h @@ -28,6 +28,7 @@ size_t strlen(const char *s); char * strcpy(char *dest, const char *src); size_t strlcpy(char *dest, const char *src, size_t n); int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); char * strchr(const char *s, int c); #endif /* _KERN_STRING_H */ -- cgit v1.2.3 From 1a73ea97557b3bab4862c7bca5904bdf406ae61f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:46:33 +0200 Subject: kern/assert: new static_assert macro --- kern/assert.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kern/assert.h b/kern/assert.h index 7c06f6a..cc8b80e 100644 --- a/kern/assert.h +++ b/kern/assert.h @@ -18,6 +18,8 @@ #ifndef _KERN_ASSERT_H #define _KERN_ASSERT_H +#define static_assert _Static_assert + #ifdef NDEBUG #define assert(expression) ((void)(expression)) #else /* NDEBUG */ -- cgit v1.2.3 From a3c67c1efaf07efe0c28978188153b8ada4f5b8d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:47:56 +0200 Subject: stdio: add getchar and putchar macros --- include/stdio.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/stdio.h b/include/stdio.h index 636304c..e1aa1a3 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -25,4 +25,10 @@ #define EOF (-1) #endif +void console_putchar(char c); +char console_getchar(void); + +#define getchar console_getchar +#define putchar console_putchar + #endif /* _STDIO_H */ -- cgit v1.2.3 From 77ed9c6cb86a97051272175a7426c326edf01bdb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:48:20 +0200 Subject: kern/hash: new module --- Makefrag.am | 1 + kern/hash.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 kern/hash.h diff --git a/Makefrag.am b/Makefrag.am index 243c224..bae713b 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -27,6 +27,7 @@ x15_SOURCES += \ kern/cpumap.h \ kern/error.c \ kern/error.h \ + kern/hash.h \ kern/init.h \ kern/intr.c \ kern/intr.h \ diff --git a/kern/hash.h b/kern/hash.h new file mode 100644 index 0000000..108d4b4 --- /dev/null +++ b/kern/hash.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2010-2017 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 3 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 . + * + * + * Hash functions for integers and strings. + * + * Integer hashing follows Thomas Wang's paper about his 32/64-bits mix + * functions : + * - https://gist.github.com/badboy/6267743 + * + * String hashing uses a variant of the djb2 algorithm with k=31, as in + * the implementation of the hashCode() method of the Java String class : + * - http://www.javamex.com/tutorials/collections/hash_function_technical.shtml + * + * Note that this algorithm isn't suitable to obtain usable 64-bits hashes + * and is expected to only serve as an array index producer. + * + * These functions all have a bits parameter that indicates the number of + * relevant bits the caller is interested in. When returning a hash, its + * value must be truncated so that it can fit in the requested bit size. + * It can be used by the implementation to select high or low bits, depending + * on their relative randomness. To get complete, unmasked hashes, use the + * HASH_ALLBITS macro. + */ + +#ifndef _KERN_HASH_H +#define _KERN_HASH_H + +#include +#include + +#include + +#ifdef __LP64__ +#define HASH_ALLBITS 64 +#define hash_long(n, bits) hash_int64(n, bits) +#else /* __LP64__ */ +static_assert(sizeof(long) == 4, "unsupported data model"); +#define HASH_ALLBITS 32 +#define hash_long(n, bits) hash_int32(n, bits) +#endif + +static inline uint32_t +hash_int32(uint32_t n, unsigned int bits) +{ + uint32_t hash; + + hash = n; + hash = ~hash + (hash << 15); + hash ^= (hash >> 12); + hash += (hash << 2); + hash ^= (hash >> 4); + hash += (hash << 3) + (hash << 11); + hash ^= (hash >> 16); + + return hash >> (32 - bits); +} + +static inline uint64_t +hash_int64(uint64_t n, unsigned int bits) +{ + uint64_t hash; + + hash = n; + hash = ~hash + (hash << 21); + hash ^= (hash >> 24); + hash += (hash << 3) + (hash << 8); + hash ^= (hash >> 14); + hash += (hash << 2) + (hash << 4); + hash ^= (hash >> 28); + hash += (hash << 31); + + return hash >> (64 - bits); +} + +static inline uintptr_t +hash_ptr(const void *ptr, unsigned int bits) +{ + if (sizeof(uintptr_t) == 8) { + return hash_int64((uintptr_t)ptr, bits); + } else { + return hash_int32((uintptr_t)ptr, bits); + } +} + +static inline unsigned long +hash_str(const char *str, unsigned int bits) +{ + unsigned long hash; + char c; + + for (hash = 0; (c = *str) != '\0'; str++) { + hash = ((hash << 5) - hash) + c; + } + + return hash & ((1 << bits) - 1); +} + +#endif /* _KERN_HASH_H */ -- cgit v1.2.3 From 3aac53f039c9c8c45509c2e915b27e1f60fe3ddc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:48:36 +0200 Subject: kern/shell: new module --- Makefrag.am | 2 + kern/kernel.c | 2 + kern/shell.c | 1177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/shell.h | 69 ++++ 4 files changed, 1250 insertions(+) create mode 100644 kern/shell.c create mode 100644 kern/shell.h diff --git a/Makefrag.am b/Makefrag.am index bae713b..e9d4e63 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -71,6 +71,8 @@ x15_SOURCES += \ kern/semaphore.c \ kern/semaphore.h \ kern/semaphore_i.h \ + kern/shell.c \ + kern/shell.h \ kern/sleepq.c \ kern/sleepq.h \ kern/spinlock.c \ diff --git a/kern/kernel.c b/kern/kernel.c index 33f3e6b..05cf839 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ kernel_main(void) work_setup(); llsync_setup(); sref_setup(); + shell_setup(); vm_page_info(); #ifdef X15_RUN_TEST_MODULE diff --git a/kern/shell.c b/kern/shell.c new file mode 100644 index 0000000..c4675bf --- /dev/null +++ b/kern/shell.c @@ -0,0 +1,1177 @@ +/* + * Copyright (c) 2015 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 3 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 + +/* + * Binary exponent and size of the hash table used to store commands. + */ +#define SHELL_HTABLE_BITS 6 +#define SHELL_HTABLE_SIZE (1 << SHELL_HTABLE_BITS) + +struct shell_bucket { + struct shell_cmd *cmd; +}; + +/* + * Hash table for quick command lookup. + */ +static struct shell_bucket shell_htable[SHELL_HTABLE_SIZE]; + +#define SHELL_COMPLETION_MATCH_FMT "-16s" +#define SHELL_COMPLETION_NR_MATCHES_PER_LINE 4 + +/* + * Sorted command list. + */ +static struct shell_cmd *shell_list; + +/* + * Lock protecting access to the hash table and list of commands. + * + * Note that this lock only protects access to the commands containers, + * not the commands themselves. In particular, it is not necessary to + * hold this lock when a command is used, i.e. when accessing a command + * name, function pointer, or description. + */ +static struct mutex shell_lock; + +/* + * Escape sequence states. + * + * Here is an incomplete description of escape sequences : + * http://en.wikipedia.org/wiki/ANSI_escape_code + * + * These values must be different from 0. + */ +#define SHELL_ESC_STATE_START 1 +#define SHELL_ESC_STATE_CSI 2 + +/* + * This value changes depending on the standard used and was chosen arbitrarily. + */ +#define SHELL_ESC_SEQ_MAX_SIZE 8 + +typedef void (*shell_esc_seq_fn)(void); + +struct shell_esc_seq { + const char *str; + shell_esc_seq_fn fn; +}; + +#define SHELL_LINE_MAX_SIZE 64 + +/* + * Line containing a shell entry. + * + * The string must be nul-terminated. The size doesn't include this + * additional nul character, the same way strlen() doesn't account for it. + */ +struct shell_line { + char str[SHELL_LINE_MAX_SIZE]; + unsigned long size; +}; + +/* + * Number of entries in the history. + * + * One of these entryes is used as the current line. + */ +#define SHELL_HISTORY_SIZE 21 + +#if SHELL_HISTORY_SIZE == 0 +#error "shell history size must be non-zero" +#endif /* SHELL_HISTORY_SIZE == 0 */ + +/* + * Shell history. + * + * The history is never empty. There is always at least one entry, the + * current line, referenced by the newest (most recent) index. The array + * is used like a circular buffer, i.e. old entries are implicitely + * erased by new ones. The index references the entry used as a template + * for the current line. + */ +static struct shell_line shell_history[SHELL_HISTORY_SIZE]; +static unsigned long shell_history_newest; +static unsigned long shell_history_oldest; +static unsigned long shell_history_index; + +/* + * Cursor within the current line. + */ +static unsigned long shell_cursor; + +#define SHELL_SEPARATOR ' ' + +/* + * Commonly used backspace control characters. + * + * XXX Adjust for your needs. + */ +#define SHELL_ERASE_BS '\b' +#define SHELL_ERASE_DEL '\x7f' + +/* + * Buffer used to store the current line during argument processing. + * + * The pointers in the argv array point inside this buffer. The + * separators immediately following the arguments are replaced with + * nul characters. + */ +static char shell_tmp_line[SHELL_LINE_MAX_SIZE]; + +#define SHELL_MAX_ARGS 16 + +static int shell_argc; +static char *shell_argv[SHELL_MAX_ARGS]; + +static const char * +shell_find_word(const char *str) +{ + for (;;) { + if ((*str == '\0') || (*str != SHELL_SEPARATOR)) { + break; + } + + str++; + } + + return str; +} + +void +shell_cmd_init(struct shell_cmd *cmd, const char *name, + shell_fn_t fn, const char *usage, + const char *short_desc, const char *long_desc) +{ + cmd->ht_next = NULL; + cmd->ls_next = NULL; + cmd->name = name; + cmd->fn = fn; + cmd->usage = usage; + cmd->short_desc = short_desc; + cmd->long_desc = long_desc; +} + +static const char * +shell_cmd_name(const struct shell_cmd *cmd) +{ + return cmd->name; +} + +static inline struct shell_bucket * +shell_bucket_get(const char *name) +{ + return &shell_htable[hash_str(name, SHELL_HTABLE_BITS)]; +} + +static void +shell_cmd_acquire(void) +{ + mutex_lock(&shell_lock); +} + +static void +shell_cmd_release(void) +{ + mutex_unlock(&shell_lock); +} + +static const struct shell_cmd * +shell_cmd_lookup(const char *name) +{ + const struct shell_bucket *bucket; + const struct shell_cmd *cmd; + + shell_cmd_acquire(); + + bucket = shell_bucket_get(name); + + for (cmd = bucket->cmd; cmd != NULL; cmd = cmd->ht_next) { + if (strcmp(cmd->name, name) == 0) { + break; + } + } + + shell_cmd_release(); + + return cmd; +} + +/* + * The global lock must be acquired before calling this function. + */ +static const struct shell_cmd * +shell_cmd_match(const struct shell_cmd *cmd, const char *str, + unsigned long size) +{ + while (cmd != NULL) { + if (strncmp(cmd->name, str, size) == 0) { + return cmd; + } + + cmd = cmd->ls_next; + } + + return NULL; +} + +/* + * The global lock must be acquired before calling this function. + */ +static int +shell_cmd_complete(const char *str, unsigned long *sizep, + const struct shell_cmd **cmdp) +{ + const struct shell_cmd *cmd, *next; + unsigned long size; + + size = *sizep; + + /* + * Start with looking up a command that matches the given argument. + * If there is no match, return an error. + */ + cmd = shell_cmd_match(shell_list, str, size); + + if (cmd == NULL) { + return ERROR_INVAL; + } + + *cmdp = cmd; + + /* + * If at least one command matches, try to complete it. + * There can be two cases : + * 1/ There is one and only one match, which is directly returned. + * 2/ There are several matches, in which case the common length is + * computed. + */ + next = cmd->ls_next; + + if ((next == NULL) + || (strncmp(cmd->name, next->name, size) != 0)) { + *sizep = strlen(cmd->name); + return 0; + } + + /* + * When computing the common length, all the commands that can match + * must be evaluated. Considering the current command is the first + * that can match, the only other variable missing is the last + * command that can match. + */ + while (next->ls_next != NULL) { + if (strncmp(cmd->name, next->ls_next->name, size) != 0) { + break; + } + + next = next->ls_next; + } + + if (size == 0) { + size = 1; + } + + while ((cmd->name[size - 1] != '\0') + && (cmd->name[size - 1] == next->name[size - 1])) { + size++; + } + + size--; + *sizep = size; + return ERROR_AGAIN; +} + +/* + * The global lock must be acquired before calling this function. + */ +static void +shell_cmd_print_matches(const struct shell_cmd *cmd, unsigned long size) +{ + const struct shell_cmd *tmp; + unsigned int i; + + printf("\n"); + + for (tmp = cmd, i = 1; tmp != NULL; tmp = tmp->ls_next, i++) { + if (strncmp(cmd->name, tmp->name, size) != 0) { + break; + } + + printf("%" SHELL_COMPLETION_MATCH_FMT, tmp->name); + + if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) == 0) { + printf("\n"); + } + } + + if ((i % SHELL_COMPLETION_NR_MATCHES_PER_LINE) != 1) { + printf("\n"); + } +} + +static int +shell_cmd_check_char(char c) +{ + if (((c >= 'a') && (c <= 'z')) + || ((c >= 'A') && (c <= 'Z')) + || ((c >= '0') && (c <= '9')) + || (c == '-') + || (c == '_')) { + return 0; + } + + return ERROR_INVAL; +} + +static int +shell_cmd_check(const struct shell_cmd *cmd) +{ + unsigned long i; + int error; + + for (i = 0; cmd->name[i] != '\0'; i++) { + error = shell_cmd_check_char(cmd->name[i]); + + if (error) { + return error; + } + } + + if (i == 0) { + return ERROR_INVAL; + } + + return 0; +} + +/* + * The global lock must be acquired before calling this function. + */ +static void +shell_cmd_add_list(struct shell_cmd *cmd) +{ + struct shell_cmd *prev, *next; + + prev = shell_list; + + if ((prev == NULL) + || (strcmp(cmd->name, prev->name) < 0)) { + shell_list = cmd; + cmd->ls_next = prev; + return; + } + + for (;;) { + next = prev->ls_next; + + if ((next == NULL) + || (strcmp(cmd->name, next->name) < 0)) { + break; + } + + prev = next; + } + + prev->ls_next = cmd; + cmd->ls_next = next; +} + +/* + * The global lock must be acquired before calling this function. + */ +static int +shell_cmd_add(struct shell_cmd *cmd) +{ + struct shell_bucket *bucket; + struct shell_cmd *tmp; + + bucket = shell_bucket_get(cmd->name); + tmp = bucket->cmd; + + if (tmp == NULL) { + bucket->cmd = cmd; + goto out; + } + + for (;;) { + if (strcmp(cmd->name, tmp->name) == 0) { + printf("shell: %s: shell command name collision", cmd->name); + return ERROR_EXIST; + } + + if (tmp->ht_next == NULL) { + break; + } + + tmp = tmp->ht_next; + } + + tmp->ht_next = cmd; + +out: + shell_cmd_add_list(cmd); + return 0; +} + +int +shell_cmd_register(struct shell_cmd *cmd) +{ + int error; + + error = shell_cmd_check(cmd); + + if (error) { + return error; + } + + shell_cmd_acquire(); + error = shell_cmd_add(cmd); + shell_cmd_release(); + + return error; +} + +static inline const char * +shell_line_str(const struct shell_line *line) +{ + return line->str; +} + +static inline unsigned long +shell_line_size(const struct shell_line *line) +{ + return line->size; +} + +static inline void +shell_line_reset(struct shell_line *line) +{ + line->str[0] = '\0'; + line->size = 0; +} + +static inline void +shell_line_copy(struct shell_line *dest, const struct shell_line *src) +{ + strcpy(dest->str, src->str); + dest->size = src->size; +} + +static inline int +shell_line_cmp(const struct shell_line *a, const struct shell_line *b) +{ + return strcmp(a->str, b->str); +} + +static int +shell_line_insert(struct shell_line *line, unsigned long index, char c) +{ + unsigned long remaining_chars; + + if (index > line->size) { + return ERROR_INVAL; + } + + if ((line->size + 1) == sizeof(line->str)) { + return ERROR_NOMEM; + } + + remaining_chars = line->size - index; + + if (remaining_chars != 0) { + memmove(&line->str[index + 1], &line->str[index], remaining_chars); + } + + line->str[index] = c; + line->size++; + line->str[line->size] = '\0'; + return 0; +} + +static int +shell_line_erase(struct shell_line *line, unsigned long index) +{ + unsigned long remaining_chars; + + if (index >= line->size) { + return ERROR_INVAL; + } + + remaining_chars = line->size - index - 1; + + if (remaining_chars != 0) { + memmove(&line->str[index], &line->str[index + 1], remaining_chars); + } + + line->size--; + line->str[line->size] = '\0'; + return 0; +} + +static struct shell_line * +shell_history_get(unsigned long index) +{ + return &shell_history[index % ARRAY_SIZE(shell_history)]; +} + +static struct shell_line * +shell_history_get_newest(void) +{ + return shell_history_get(shell_history_newest); +} + +static struct shell_line * +shell_history_get_index(void) +{ + return shell_history_get(shell_history_index); +} + +static void +shell_history_reset_index(void) +{ + shell_history_index = shell_history_newest; +} + +static inline int +shell_history_same_newest(void) +{ + return (shell_history_newest != shell_history_oldest) + && shell_line_cmp(shell_history_get_newest(), + shell_history_get(shell_history_newest - 1)) == 0; +} + +static void +shell_history_push(void) +{ + if ((shell_line_size(shell_history_get_newest()) == 0) + || shell_history_same_newest()) { + shell_history_reset_index(); + return; + } + + shell_history_newest++; + shell_history_reset_index(); + + /* Mind integer overflows */ + if ((shell_history_newest - shell_history_oldest) + >= ARRAY_SIZE(shell_history)) { + shell_history_oldest = shell_history_newest + - ARRAY_SIZE(shell_history) + 1; + } +} + +static void +shell_history_back(void) +{ + if (shell_history_index == shell_history_oldest) { + return; + } + + shell_history_index--; + shell_line_copy(shell_history_get_newest(), shell_history_get_index()); +} + +static void +shell_history_forward(void) +{ + if (shell_history_index == shell_history_newest) { + return; + } + + shell_history_index++; + + if (shell_history_index == shell_history_newest) { + shell_line_reset(shell_history_get_newest()); + } else { + shell_line_copy(shell_history_get_newest(), shell_history_get_index()); + } +} + +static void +shell_cmd_help(int argc, char *argv[]) +{ + const struct shell_cmd *cmd; + + if (argc > 2) { + argc = 2; + argv[1] = "help"; + } + + if (argc == 2) { + cmd = shell_cmd_lookup(argv[1]); + + if (cmd == NULL) { + printf("shell: help: %s: command not found\n", argv[1]); + return; + } + + printf("usage: %s\n%s\n", cmd->usage, cmd->short_desc); + + if (cmd->long_desc != NULL) { + printf("\n%s\n", cmd->long_desc); + } + + return; + } + + shell_cmd_acquire(); + + for (cmd = shell_list; cmd != NULL; cmd = cmd->ls_next) { + printf("%13s %s\n", cmd->name, cmd->short_desc); + } + + shell_cmd_release(); +} + +static void +shell_cmd_history(int argc, char *argv[]) +{ + unsigned long i; + + (void)argc; + (void)argv; + + /* Mind integer overflows */ + for (i = shell_history_oldest; i != shell_history_newest; i++) { + printf("%6lu %s\n", i - shell_history_oldest, + shell_line_str(shell_history_get(i))); + } +} + +static struct shell_cmd shell_default_cmds[] = { + SHELL_CMD_INITIALIZER("help", shell_cmd_help, + "help [command]", + "obtain help about shell commands"), + SHELL_CMD_INITIALIZER("history", shell_cmd_history, + "history", + "display history list"), +}; + +static void +shell_prompt(void) +{ + printf("shell> "); +} + +static void +shell_reset(void) +{ + shell_line_reset(shell_history_get_newest()); + shell_cursor = 0; + shell_prompt(); +} + +static void +shell_erase(void) +{ + struct shell_line *current_line; + unsigned long remaining_chars; + + current_line = shell_history_get_newest(); + remaining_chars = shell_line_size(current_line); + + while (shell_cursor != remaining_chars) { + putchar(' '); + shell_cursor++; + } + + while (remaining_chars != 0) { + printf("\b \b"); + remaining_chars--; + } + + shell_cursor = 0; +} + +static void +shell_restore(void) +{ + struct shell_line *current_line; + + current_line = shell_history_get_newest(); + printf("%s", shell_line_str(current_line)); + shell_cursor = shell_line_size(current_line); +} + +static int +shell_is_ctrl_char(char c) +{ + return ((c < ' ') || (c >= 0x7f)); +} + +static void +shell_process_left(void) +{ + if (shell_cursor == 0) { + return; + } + + shell_cursor--; + printf("\e[1D"); +} + +static int +shell_process_right(void) +{ + if (shell_cursor >= shell_line_size(shell_history_get_newest())) { + return ERROR_AGAIN; + } + + shell_cursor++; + printf("\e[1C"); + return 0; +} + +static void +shell_process_up(void) +{ + shell_erase(); + shell_history_back(); + shell_restore(); +} + +static void +shell_process_down(void) +{ + shell_erase(); + shell_history_forward(); + shell_restore(); +} + +static void +shell_process_backspace(void) +{ + struct shell_line *current_line; + unsigned long remaining_chars; + int error; + + current_line = shell_history_get_newest(); + error = shell_line_erase(current_line, shell_cursor - 1); + + if (error) { + return; + } + + shell_cursor--; + printf("\b%s ", shell_line_str(current_line) + shell_cursor); + remaining_chars = shell_line_size(current_line) - shell_cursor + 1; + + while (remaining_chars != 0) { + putchar('\b'); + remaining_chars--; + } +} + +static int +shell_process_raw_char(char c) +{ + struct shell_line *current_line; + unsigned long remaining_chars; + int error; + + current_line = shell_history_get_newest(); + error = shell_line_insert(current_line, shell_cursor, c); + + if (error) { + printf("\nshell: line too long\n"); + return error; + } + + shell_cursor++; + + if (shell_cursor == shell_line_size(current_line)) { + putchar(c); + goto out; + } + + printf("%s", shell_line_str(current_line) + shell_cursor - 1); + remaining_chars = shell_line_size(current_line) - shell_cursor; + + while (remaining_chars != 0) { + putchar('\b'); + remaining_chars--; + } + +out: + return 0; +} + +static int +shell_process_tabulation(void) +{ + const struct shell_cmd *cmd = NULL; /* GCC */ + const char *name, *str, *word; + unsigned long i, size, cmd_cursor; + int error; + + shell_cmd_acquire(); + + str = shell_line_str(shell_history_get_newest()); + word = shell_find_word(str); + size = shell_cursor - (word - str); + cmd_cursor = shell_cursor - size; + + error = shell_cmd_complete(word, &size, &cmd); + + if (error && (error != ERROR_AGAIN)) { + error = 0; + goto out; + } + + if (error == ERROR_AGAIN) { + unsigned long cursor; + + cursor = shell_cursor; + shell_cmd_print_matches(cmd, size); + shell_prompt(); + shell_restore(); + + /* Keep existing arguments as they are */ + while (shell_cursor != cursor) { + shell_process_left(); + } + } + + name = shell_cmd_name(cmd); + + while (shell_cursor != cmd_cursor) { + shell_process_backspace(); + } + + for (i = 0; i < size; i++) { + error = shell_process_raw_char(name[i]); + + if (error) { + goto out; + } + } + + error = 0; + +out: + shell_cmd_release(); + return error; +} + +static void +shell_esc_seq_up(void) +{ + shell_process_up(); +} + +static void +shell_esc_seq_down(void) +{ + shell_process_down(); +} + +static void +shell_esc_seq_next(void) +{ + shell_process_right(); +} + +static void +shell_esc_seq_prev(void) +{ + shell_process_left(); +} + +static void +shell_esc_seq_home(void) +{ + while (shell_cursor != 0) { + shell_process_left(); + } +} + +static void +shell_esc_seq_del(void) +{ + int error; + + error = shell_process_right(); + + if (error) { + return; + } + + shell_process_backspace(); +} + +static void +shell_esc_seq_end(void) +{ + unsigned long size; + + size = shell_line_size(shell_history_get_newest()); + + while (shell_cursor < size) { + shell_process_right(); + } +} + +static const struct shell_esc_seq shell_esc_seqs[] = { + { "A", shell_esc_seq_up }, + { "B", shell_esc_seq_down }, + { "C", shell_esc_seq_next }, + { "D", shell_esc_seq_prev }, + { "H", shell_esc_seq_home }, + { "1~", shell_esc_seq_home }, + { "3~", shell_esc_seq_del }, + { "F", shell_esc_seq_end }, + { "4~", shell_esc_seq_end }, +}; + +static const struct shell_esc_seq * +shell_esc_seq_lookup(const char *str) +{ + unsigned long i; + + for (i = 0; i < ARRAY_SIZE(shell_esc_seqs); i++) { + if (strcmp(shell_esc_seqs[i].str, str) == 0) { + return &shell_esc_seqs[i]; + } + } + + return NULL; +} + +/* + * Process a single escape sequence character. + * + * Return the next escape state or 0 if the sequence is complete. + */ +static int +shell_process_esc_sequence(char c) +{ + static char str[SHELL_ESC_SEQ_MAX_SIZE], *ptr = str; + + const struct shell_esc_seq *seq; + uintptr_t index; + + index = ptr - str; + + if (index >= (ARRAY_SIZE(str) - 1)) { + printf("shell: escape sequence too long\n"); + goto reset; + } + + *ptr = c; + ptr++; + *ptr = '\0'; + + if ((c >= '@') && (c <= '~')) { + seq = shell_esc_seq_lookup(str); + + if (seq != NULL) { + seq->fn(); + } + + goto reset; + } + + return SHELL_ESC_STATE_CSI; + +reset: + ptr = str; + return 0; +} + +static int +shell_process_args(void) +{ + unsigned long i; + char c, prev; + int j; + + snprintf(shell_tmp_line, sizeof(shell_tmp_line), "%s", + shell_line_str(shell_history_get_newest())); + + for (i = 0, j = 0, prev = SHELL_SEPARATOR; + (c = shell_tmp_line[i]) != '\0'; + i++, prev = c) { + if (c == SHELL_SEPARATOR) { + if (prev != SHELL_SEPARATOR) { + shell_tmp_line[i] = '\0'; + } + } else { + if (prev == SHELL_SEPARATOR) { + shell_argv[j] = &shell_tmp_line[i]; + j++; + + if (j == ARRAY_SIZE(shell_argv)) { + printf("shell: too many arguments\n"); + return ERROR_INVAL; + } + + shell_argv[j] = NULL; + } + } + } + + shell_argc = j; + return 0; +} + +static void +shell_process_line(void) +{ + const struct shell_cmd *cmd; + int error; + + cmd = NULL; + error = shell_process_args(); + + if (error) { + goto out; + } + + if (shell_argc == 0) { + goto out; + } + + cmd = shell_cmd_lookup(shell_argv[0]); + + if (cmd == NULL) { + printf("shell: %s: command not found\n", shell_argv[0]); + goto out; + } + +out: + shell_history_push(); + + if (cmd != NULL) { + cmd->fn(shell_argc, shell_argv); + } +} + +/* + * Process a single control character. + * + * Return an error if the caller should reset the current line state. + */ +static int +shell_process_ctrl_char(char c) +{ + switch (c) { + case SHELL_ERASE_BS: + case SHELL_ERASE_DEL: + shell_process_backspace(); + break; + case '\t': + return shell_process_tabulation(); + case '\n': + case '\r': + putchar('\n'); + shell_process_line(); + return ERROR_AGAIN; + default: + return 0; + } + + return 0; +} + +static void +shell_run(void *arg) +{ + int c, error, escape; + + (void)arg; + + for (;;) { + shell_reset(); + escape = 0; + + for (;;) { + c = getchar(); + + if (escape) { + switch (escape) { + case SHELL_ESC_STATE_START: + /* XXX CSI and SS3 sequence processing is the same */ + if ((c == '[') || (c == 'O')) { + escape = SHELL_ESC_STATE_CSI; + } else { + escape = 0; + } + + break; + case SHELL_ESC_STATE_CSI: + escape = shell_process_esc_sequence(c); + break; + default: + escape = 0; + } + + error = 0; + } else if (shell_is_ctrl_char(c)) { + if (c == '\e') { + escape = SHELL_ESC_STATE_START; + error = 0; + } else { + error = shell_process_ctrl_char(c); + + if (error) { + break; + } + } + } else { + error = shell_process_raw_char(c); + } + + if (error) { + break; + } + } + } +} + +void __init +shell_setup(void) +{ + struct thread_attr attr; + struct thread *thread; + unsigned long i; + int error; + + mutex_init(&shell_lock); + + for (i = 0; i < ARRAY_SIZE(shell_default_cmds); i++) { + error = shell_cmd_register(&shell_default_cmds[i]); + error_check(error, "shell_cmd_register"); + } + + thread_attr_init(&attr, THREAD_KERNEL_PREFIX "shell"); + thread_attr_set_detached(&attr); + error = thread_create(&thread, &attr, shell_run, NULL); + error_check(error, "thread_create"); +} diff --git a/kern/shell.h b/kern/shell.h new file mode 100644 index 0000000..5ff4920 --- /dev/null +++ b/kern/shell.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 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 3 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 . + * + * + * Minimalist shell for embedded systems. + */ + +#ifndef _KERN_SHELL_H +#define _KERN_SHELL_H + +typedef void (*shell_fn_t)(int argc, char *argv[]); + +struct shell_cmd { + struct shell_cmd *ht_next; + struct shell_cmd *ls_next; + const char *name; + shell_fn_t fn; + const char *usage; + const char *short_desc; + const char *long_desc; +}; + +/* + * Static shell command initializers. + */ +#define SHELL_CMD_INITIALIZER(name, fn, usage, short_desc) \ + { NULL, NULL, name, fn, usage, short_desc, NULL } +#define SHELL_CMD_INITIALIZER2(name, fn, usage, short_desc, long_desc) \ + { NULL, NULL, name, fn, usage, short_desc, long_desc } + +/* + * Initialize a shell command structure. + */ +void shell_cmd_init(struct shell_cmd *cmd, const char *name, + shell_fn_t fn, const char *usage, + const char *short_desc, const char *long_desc); + +/* + * Initialize the shell module. + * + * On return, shell commands can be registered. + */ +void shell_setup(void); + +/* + * Register a shell command. + * + * The command name must be unique. It must not include characters outside + * the [a-zA-Z0-9-_] class. + * + * The structure passed when calling this function is directly reused by + * the shell module and must persist in memory. + */ +int shell_cmd_register(struct shell_cmd *cmd); + +#endif /* _KERN_SHELL_H */ -- cgit v1.2.3 From 16fd990520cd3ff7f12dd5c37ca8d1f64999ba0c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 17:49:26 +0200 Subject: x86/cpu: remove apic feature requirement --- arch/x86/machine/cpu.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index fbc1f95..eaa43ae 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -576,11 +576,6 @@ cpu_check(const struct cpu *cpu) cpu_panic_on_missing_feature("fpu"); } - /* TODO: support UP with legacy PIC machines */ - if (!(cpu->features2 & CPU_FEATURE2_APIC)) { - cpu_panic_on_missing_feature("apic"); - } - /* * The compiler is expected to produce cmpxchg8b instructions to * perform 64-bits atomic operations on a 32-bits processor. Clang -- cgit v1.2.3 From 3bdbab08f6d21f71a69ad3ba317b27ed816ccd9b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 20:03:35 +0200 Subject: x86/lapic: fix initialization on BSP --- arch/x86/machine/lapic.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 444e661..6bd1d72 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -200,10 +200,11 @@ lapic_write(volatile struct lapic_register *r, uint32_t value) } static void __init -lapic_setup_timer(void) +lapic_compute_freq(void) { uint32_t c1, c2; + lapic_write(&lapic_map->svr, LAPIC_SVR_SOFT_EN | TRAP_LAPIC_SPURIOUS); lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1); /* The APIC timer counter should never wrap around here */ @@ -215,6 +216,7 @@ lapic_setup_timer(void) printf("lapic: bus frequency: %u.%02u MHz\n", lapic_bus_freq / 1000000, lapic_bus_freq % 1000000); lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); + lapic_write(&lapic_map->svr, 0); } void @@ -228,6 +230,7 @@ lapic_setup_registers(void) { /* * LVT mask bits can only be cleared when the local APIC is enabled. + * They are kept disabled while the local APIC is disabled. */ lapic_write(&lapic_map->svr, LAPIC_SVR_SOFT_EN | TRAP_LAPIC_SPURIOUS); lapic_write(&lapic_map->tpr, 0); @@ -273,8 +276,8 @@ lapic_setup(uint32_t map_addr) panic("lapic: external local APIC not supported"); } + lapic_compute_freq(); lapic_setup_registers(); - lapic_setup_timer(); lapic_initialized = true; } -- cgit v1.2.3 From cebac3e7574d2772e98775dd03c23b8118a6c807 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 28 May 2017 20:03:49 +0200 Subject: kern/intr: route all interrupts to BSP APIC support needs to be improved before interrupts can be routed to any processor. --- kern/intr.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/kern/intr.c b/kern/intr.c index 2bbfb7c..fe69e3e 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -85,17 +85,15 @@ static struct list intr_ctls; static struct kmem_cache intr_handler_cache; -/* - * Next processor to route interrupts to. - * - * TODO Replace the simple current round-robin policy with a better one. - */ -static unsigned int intr_next_cpu; - static unsigned int intr_select_cpu(void) { - return atomic_fetch_add(&intr_next_cpu, 1, ATOMIC_RELAXED) % cpu_count(); + /* + * TODO Interrupt routing. + * Although the interface supports it, there are currently problems + * with the I/O APIC that need to be solved first. + */ + return 0; } static int -- cgit v1.2.3 From e30e2f39a145d61ddf3d3eb450b5771a90e8c0f2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 29 May 2017 11:59:15 +0200 Subject: x86/atkbd: remove keyboard communication Not communicating with the keyboard helps against buggy/incomplete legacy emulation. --- arch/x86/machine/atkbd.c | 75 ++++++------------------------------------------ 1 file changed, 9 insertions(+), 66 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 362b834..ad66e21 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -13,6 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * + * Note that this driver is only intended to provide enough functionality + * for the diagnostics shell. As a result, some features, especially those + * that may not correctly be emulated for USB keyboards, will not be + * supported. This includes any communication with the keyboard itself. */ #include @@ -32,17 +38,6 @@ #define ATKBD_PORT_STATUS 0x64 #define ATKBD_PORT_CMD 0x64 -#define ATKBD_REQ_LEDS 0xed -#define ATKBD_REQ_RESET 0xff - -#define ATKBD_REP_PASS 0xaa -#define ATKBD_REP_ACK 0xfa -#define ATKBD_REP_RESEND 0xfe - -#define ATKBD_LEDS_SCROLLLOCK 0x01 -#define ATKBD_LEDS_NUMLOCK 0x02 -#define ATKBD_LEDS_CAPSLOCK 0x04 - #define ATKBD_STATUS_OUT_FULL 0x01 #define ATKBD_STATUS_IN_FULL 0x02 #define ATKBD_STATUS_PARITY_ERROR 0x80 @@ -511,31 +506,11 @@ static void atkbd_flush(void) { uint8_t status; - - atkbd_read(&status, false); -} - -static void __init -atkbd_reset_kbd(void) -{ - uint8_t reply; int error; -resend: - atkbd_write(ATKBD_REQ_RESET); - error = atkbd_read(&reply, true); - - if (error) { - return; - } - - if (reply == ATKBD_REP_RESEND) { - goto resend; - } - - if ((reply != ATKBD_REP_PASS) && (reply != ATKBD_REP_ACK)) { - printf("atkbd: warning: unable to reset keyboard\n"); - } + do { + error = atkbd_read(&status, false); + } while (!error); } static int __init @@ -588,46 +563,16 @@ atkbd_enable(void) return 0; } -static void -atkbd_set_leds(void) -{ - uint8_t leds, reply; - int error; - - leds = ((atkbd_flags & ATKBD_KF_NUMLOCK) ? ATKBD_LEDS_NUMLOCK : 0) - | ((atkbd_flags & ATKBD_KF_CAPSLOCK) ? ATKBD_LEDS_CAPSLOCK : 0) - | ((atkbd_flags & ATKBD_KF_SCROLLLOCK) ? ATKBD_LEDS_SCROLLLOCK : 0); - -resend: - atkbd_write(ATKBD_REQ_LEDS); - atkbd_write(leds); - error = atkbd_read(&reply, true); - - if (error) { - return; - } - - if (reply == ATKBD_REP_RESEND) { - goto resend; - } - - if (reply != ATKBD_REP_ACK) { - printf("atkbd: warning: invalid reply from keyboard\n"); - } -} - static void atkbd_toggle_numlock(void) { atkbd_flags ^= ATKBD_KF_NUMLOCK; - atkbd_set_leds(); } static void atkbd_toggle_capslock(void) { atkbd_flags ^= ATKBD_KF_CAPSLOCK; - atkbd_set_leds(); } static void @@ -806,6 +751,4 @@ atkbd_setup(void) if (error) { return; } - - atkbd_reset_kbd(); } -- cgit v1.2.3 From 1c6a75ce939d2e7ffd12a3edac7618eb63447e09 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 29 May 2017 18:34:46 +0200 Subject: kern/atomic: provide _acq_rel macros instead of _seq_cst --- kern/atomic.h | 6 +++--- kern/panic.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kern/atomic.h b/kern/atomic.h index 9e26ad5..75b0701 100644 --- a/kern/atomic.h +++ b/kern/atomic.h @@ -122,12 +122,12 @@ MACRO_END #define atomic_cas_release(ptr, oval, nval) \ atomic_cas(ptr, oval, nval, ATOMIC_RELEASE) -#define atomic_cas_seq_cst(ptr, oval, nval) \ - atomic_cas(ptr, oval, nval, ATOMIC_SEQ_CST) +#define atomic_cas_acq_rel(ptr, oval, nval) \ + atomic_cas(ptr, oval, nval, ATOMIC_ACQ_REL) #define atomic_swap_acquire(ptr, val) atomic_swap(ptr, val, ATOMIC_ACQUIRE) #define atomic_swap_release(ptr, val) atomic_swap(ptr, val, ATOMIC_RELEASE) -#define atomic_swap_seq_cst(ptr, val) atomic_swap(ptr, val, ATOMIC_SEQ_CST) +#define atomic_swap_acq_rel(ptr, val) atomic_swap(ptr, val, ATOMIC_ACQ_REL) #define atomic_fetch_add_acq_rel(ptr, val) \ atomic_fetch_add(ptr, val, ATOMIC_ACQ_REL) diff --git a/kern/panic.c b/kern/panic.c index 34d8c13..9e7d1d5 100644 --- a/kern/panic.c +++ b/kern/panic.c @@ -31,7 +31,7 @@ panic(const char *format, ...) va_list list; unsigned long already_done; - already_done = atomic_swap_seq_cst(&panic_done, 1); + already_done = atomic_swap(&panic_done, 1, ATOMIC_SEQ_CST); if (already_done) { for (;;) { -- cgit v1.2.3 From fb9d72a5a9ddc571248025204af63f0987813d50 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 29 May 2017 18:35:51 +0200 Subject: kern/spinlock: replace xxx_once macros with atomic accessors --- kern/spinlock.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/kern/spinlock.c b/kern/spinlock.c index 34b78ba..3ec36b3 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -193,9 +193,9 @@ spinlock_store_first_qid(struct spinlock *lock, unsigned int newqid) newqid <<= SPINLOCK_QID_MAX_BITS; do { - oldval = read_once(lock->value); + oldval = atomic_load(&lock->value, ATOMIC_RELAXED); newval = newqid | (oldval & SPINLOCK_QID_MASK); - prev = atomic_cas_acquire(&lock->value, oldval, newval); + prev = atomic_cas_release(&lock->value, oldval, newval); } while (prev != oldval); } @@ -204,7 +204,7 @@ spinlock_load_first_qid(const struct spinlock *lock) { unsigned int value; - value = read_once(lock->value); + value = atomic_load(&lock->value, ATOMIC_ACQUIRE); return (value >> SPINLOCK_QID_MAX_BITS) & SPINLOCK_QID_MASK; } @@ -216,10 +216,10 @@ spinlock_swap_last_qid(struct spinlock *lock, unsigned int newqid) assert(newqid < SPINLOCK_QID_MAX); do { - oldval = read_once(lock->value); + oldval = atomic_load(&lock->value, ATOMIC_RELAXED); newval = (oldval & (SPINLOCK_QID_MASK << SPINLOCK_QID_MAX_BITS)) | newqid; - prev = atomic_cas_acquire(&lock->value, oldval, newval); + prev = atomic_cas_acq_rel(&lock->value, oldval, newval); } while (prev != oldval); return prev & SPINLOCK_QID_MASK; @@ -230,7 +230,8 @@ spinlock_try_downgrade(struct spinlock *lock, unsigned int oldqid) { unsigned int prev; - prev = atomic_cas_acquire(&lock->value, oldqid, SPINLOCK_QID_LOCKED); + prev = atomic_cas(&lock->value, oldqid, + SPINLOCK_QID_LOCKED, ATOMIC_RELAXED); assert((prev >> SPINLOCK_QID_MAX_BITS) == 0); assert(prev != SPINLOCK_QID_NULL); @@ -263,7 +264,7 @@ spinlock_lock_slow(struct spinlock *lock) spinlock_store_first_qid(lock, qid); } else { prev_qnode = spinlock_get_remote_qnode(prev_qid); - write_once(prev_qnode->next_qid, qid); + atomic_store(&prev_qnode->next_qid, qid, ATOMIC_RELAXED); } for (;;) { @@ -286,7 +287,7 @@ spinlock_lock_slow(struct spinlock *lock) } for (;;) { - next_qid = read_once(qnode->next_qid); + next_qid = atomic_load(&qnode->next_qid, ATOMIC_RELAXED); if (next_qid != SPINLOCK_QID_NULL) { break; -- cgit v1.2.3 From cf91efd372f25b4724a5582bae7228e24917e73d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 29 May 2017 18:36:09 +0200 Subject: kern/semaphore: replace xxx_once macros with atomic accessors --- kern/semaphore.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kern/semaphore.h b/kern/semaphore.h index 0111185..d7219b4 100644 --- a/kern/semaphore.h +++ b/kern/semaphore.h @@ -33,6 +33,7 @@ #define _KERN_SEMAPHORE_H #include +#include #include #include @@ -117,7 +118,7 @@ semaphore_post(struct semaphore *semaphore) static inline unsigned int semaphore_getvalue(const struct semaphore *semaphore) { - return read_once(semaphore->value); + return atomic_load(&semaphore->value, ATOMIC_RELAXED); } #endif /* _KERN_SEMAPHORE_H */ -- cgit v1.2.3 From 4eaa58c85eec654eb8bf8e002b3f3a419f5ce16b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 29 May 2017 18:36:19 +0200 Subject: kern/macros: remove the read_once and write_once macros --- kern/macros.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/kern/macros.h b/kern/macros.h index f3eb107..a5b7b03 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -55,9 +55,6 @@ #define structof(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) -#define read_once(x) (*(volatile typeof(x) *)&(x)) -#define write_once(x, v) (read_once(x) = (v)) - #define alignof(x) __alignof__(x) #define likely(expr) __builtin_expect(!!(expr), 1) -- cgit v1.2.3 From 436fe6cb9711401fcc1f1d8fc29025f1aec5b774 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 30 May 2017 22:16:59 +0200 Subject: Move assert.h to the include directory This turns assert.h into a standard header. --- Makefrag.am | 2 +- arch/x86/machine/acpimp.c | 2 +- arch/x86/machine/biosmem.c | 2 +- arch/x86/machine/cpu.c | 2 +- arch/x86/machine/ioapic.c | 2 +- arch/x86/machine/lapic.c | 2 +- arch/x86/machine/pic.c | 2 +- arch/x86/machine/pit.c | 3 ++- arch/x86/machine/pmap.c | 2 +- arch/x86/machine/tcb.h | 3 ++- arch/x86/machine/trap.c | 2 +- arch/x86/machine/uart.c | 2 +- include/assert.h | 43 +++++++++++++++++++++++++++++++++++++++++++ kern/arg.c | 2 +- kern/assert.h | 43 ------------------------------------------- kern/cbuf.c | 3 ++- kern/condition.c | 2 +- kern/console.c | 2 +- kern/hash.h | 3 +-- kern/kmem.c | 2 +- kern/llsync.c | 2 +- kern/llsync_i.h | 3 ++- kern/log2.h | 3 ++- kern/mutex.h | 3 ++- kern/mutex_i.h | 3 ++- kern/percpu.c | 2 +- kern/percpu.h | 2 +- kern/rbtree.c | 2 +- kern/rbtree.h | 2 +- kern/rbtree_i.h | 2 +- kern/rdxtree.c | 2 +- kern/rtmutex.c | 2 +- kern/rtmutex.h | 2 +- kern/rtmutex_i.h | 2 +- kern/semaphore.h | 3 ++- kern/semaphore_i.h | 3 ++- kern/sleepq.c | 2 +- kern/spinlock.c | 2 +- kern/spinlock_i.h | 2 +- kern/sref.c | 2 +- kern/thread.c | 2 +- kern/thread.h | 2 +- kern/turnstile.c | 2 +- kern/work.c | 2 +- kern/xcall.c | 2 +- vm/vm_kmem.c | 2 +- vm/vm_map.c | 2 +- vm/vm_page.c | 2 +- vm/vm_page.h | 2 +- 49 files changed, 99 insertions(+), 91 deletions(-) create mode 100644 include/assert.h delete mode 100644 kern/assert.h diff --git a/Makefrag.am b/Makefrag.am index e9d4e63..0fabdc2 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -4,13 +4,13 @@ include doc/Makefrag.am EXTRA_DIST += tools/qemu.sh x15_SOURCES += \ + include/assert.h \ include/stdio.h \ include/string.h x15_SOURCES += \ kern/arg.c \ kern/arg.h \ - kern/assert.h \ kern/atomic.h \ kern/bitmap.c \ kern/bitmap.h \ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c index bba2b8c..0af1ae7 100644 --- a/arch/x86/machine/acpimp.c +++ b/arch/x86/machine/acpimp.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 65329a0..bfa22af 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -15,13 +15,13 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index eaa43ae..7c60849 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 66178b3..d48cc30 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -15,11 +15,11 @@ * along with this program. If not, see . */ +#include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 6bd1d72..148a2a9 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index 9084abc..52f9461 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -15,9 +15,9 @@ * along with this program. If not, see . */ +#include #include -#include #include #include #include diff --git a/arch/x86/machine/pit.c b/arch/x86/machine/pit.c index bf53ee1..c31c7e0 100644 --- a/arch/x86/machine/pit.c +++ b/arch/x86/machine/pit.c @@ -15,7 +15,8 @@ * along with this program. If not, see . */ -#include +#include + #include #include #include diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 1de9b09..1aab9cc 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -18,11 +18,11 @@ * TODO Review locking. */ +#include #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 5f45585..914ec9d 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -21,7 +21,8 @@ #ifndef _X86_TCB_H #define _X86_TCB_H -#include +#include + #include #include diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 3de4fd2..e033b35 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -19,10 +19,10 @@ * additional configuration and resources to be properly handled. */ +#include #include #include -#include #include #include #include diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 82b6c83..24fcace 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -18,10 +18,10 @@ * TODO Make serial line parameters configurable. */ +#include #include #include -#include #include #include #include diff --git a/include/assert.h b/include/assert.h new file mode 100644 index 0000000..8fdc2fe --- /dev/null +++ b/include/assert.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010 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 3 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 _ASSERT_H +#define _ASSERT_H + +#define static_assert _Static_assert + +#ifdef NDEBUG +#define assert(expression) ((void)(expression)) +#else /* NDEBUG */ + +#include +#include + +/* + * Panic if the given expression is false. + */ +#define assert(expression) \ +MACRO_BEGIN \ + if (unlikely(!(expression))) { \ + panic("assertion (%s) failed in %s:%d, function %s()", \ + __QUOTE(expression), __FILE__, __LINE__, __func__); \ + } \ +MACRO_END + +#endif /* NDEBUG */ + +#endif /* _ASSERT_H */ diff --git a/kern/arg.c b/kern/arg.c index 02ed4b7..1278812 100644 --- a/kern/arg.c +++ b/kern/arg.c @@ -15,13 +15,13 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/kern/assert.h b/kern/assert.h deleted file mode 100644 index cc8b80e..0000000 --- a/kern/assert.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2010 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 3 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 _KERN_ASSERT_H -#define _KERN_ASSERT_H - -#define static_assert _Static_assert - -#ifdef NDEBUG -#define assert(expression) ((void)(expression)) -#else /* NDEBUG */ - -#include -#include - -/* - * Panic if the given expression is false. - */ -#define assert(expression) \ -MACRO_BEGIN \ - if (unlikely(!(expression))) { \ - panic("assertion (%s) failed in %s:%d, function %s()", \ - __QUOTE(expression), __FILE__, __LINE__, __func__); \ - } \ -MACRO_END - -#endif /* NDEBUG */ - -#endif /* _KERN_ASSERT_H */ diff --git a/kern/cbuf.c b/kern/cbuf.c index 17369af..5c636e5 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -15,7 +15,8 @@ * along with this program. If not, see . */ -#include +#include + #include #include #include diff --git a/kern/condition.c b/kern/condition.c index 4a837f4..c8ea5f3 100644 --- a/kern/condition.c +++ b/kern/condition.c @@ -18,10 +18,10 @@ * Locking order : mutex -> sleep queue */ +#include #include #include -#include #include #include #include diff --git a/kern/console.c b/kern/console.c index c82e7dc..e8c0c28 100644 --- a/kern/console.c +++ b/kern/console.c @@ -15,13 +15,13 @@ * along with this program. If not, see . */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/kern/hash.h b/kern/hash.h index 108d4b4..db39996 100644 --- a/kern/hash.h +++ b/kern/hash.h @@ -39,11 +39,10 @@ #ifndef _KERN_HASH_H #define _KERN_HASH_H +#include #include #include -#include - #ifdef __LP64__ #define HASH_ALLBITS 64 #define hash_long(n, bits) hash_int64(n, bits) diff --git a/kern/kmem.c b/kern/kmem.c index 5cce558..1f822b8 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -41,13 +41,13 @@ * TODO Rework the CPU pool layer to use the SLQB algorithm by Nick Piggin. */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/kern/llsync.c b/kern/llsync.c index 1942576..1c5086f 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -32,11 +32,11 @@ * TODO Gracefully handle large amounts of deferred works. */ +#include #include #include #include -#include #include #include #include diff --git a/kern/llsync_i.h b/kern/llsync_i.h index 6bc1bf1..12e2861 100644 --- a/kern/llsync_i.h +++ b/kern/llsync_i.h @@ -18,7 +18,8 @@ #ifndef _KERN_LLSYNC_I_H #define _KERN_LLSYNC_I_H -#include +#include + #include #include #include diff --git a/kern/log2.h b/kern/log2.h index 0a3768a..ebcf2f3 100644 --- a/kern/log2.h +++ b/kern/log2.h @@ -21,7 +21,8 @@ #ifndef _KERN_LOG2_H #define _KERN_LOG2_H -#include +#include + #include static inline unsigned int diff --git a/kern/mutex.h b/kern/mutex.h index 1100b77..d09a7bb 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -68,7 +68,8 @@ mutex_unlock(struct mutex *mutex) #else /* X15_MUTEX_PI */ -#include +#include + #include #include #include diff --git a/kern/mutex_i.h b/kern/mutex_i.h index a4a40eb..3b36dcd 100644 --- a/kern/mutex_i.h +++ b/kern/mutex_i.h @@ -20,7 +20,8 @@ #ifndef X15_MUTEX_PI -#include +#include + #include #include diff --git a/kern/percpu.c b/kern/percpu.c index 5e4ff82..a1f7df1 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include #include #include diff --git a/kern/percpu.h b/kern/percpu.h index 5995951..d6a8d1a 100644 --- a/kern/percpu.h +++ b/kern/percpu.h @@ -53,9 +53,9 @@ #ifndef _KERN_PERCPU_H #define _KERN_PERCPU_H +#include #include -#include #include #define PERCPU_SECTION .percpu diff --git a/kern/rbtree.c b/kern/rbtree.c index e2ea54a..adce033 100644 --- a/kern/rbtree.c +++ b/kern/rbtree.c @@ -15,10 +15,10 @@ * along with this program. If not, see . */ +#include #include #include -#include #include #include #include diff --git a/kern/rbtree.h b/kern/rbtree.h index 3808342..4ae8353 100644 --- a/kern/rbtree.h +++ b/kern/rbtree.h @@ -21,10 +21,10 @@ #ifndef _KERN_RBTREE_H #define _KERN_RBTREE_H +#include #include #include -#include #include /* diff --git a/kern/rbtree_i.h b/kern/rbtree_i.h index 9972297..944148e 100644 --- a/kern/rbtree_i.h +++ b/kern/rbtree_i.h @@ -18,10 +18,10 @@ #ifndef _KERN_RBTREE_I_H #define _KERN_RBTREE_I_H +#include #include #include -#include #include /* diff --git a/kern/rdxtree.c b/kern/rdxtree.c index 788f3ba..b518c7f 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include #include #include diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 6f639dd..09f011c 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -15,10 +15,10 @@ * along with this program. If not, see . */ +#include #include #include -#include #include #include #include diff --git a/kern/rtmutex.h b/kern/rtmutex.h index f64274d..ec79afa 100644 --- a/kern/rtmutex.h +++ b/kern/rtmutex.h @@ -24,9 +24,9 @@ #ifndef _KERN_RTMUTEX_H #define _KERN_RTMUTEX_H +#include #include -#include #include #include #include diff --git a/kern/rtmutex_i.h b/kern/rtmutex_i.h index 2f2cc17..984cfd1 100644 --- a/kern/rtmutex_i.h +++ b/kern/rtmutex_i.h @@ -18,10 +18,10 @@ #ifndef _KERN_RTMUTEX_I_H #define _KERN_RTMUTEX_I_H +#include #include #include -#include #include #include #include diff --git a/kern/semaphore.h b/kern/semaphore.h index d7219b4..e08927e 100644 --- a/kern/semaphore.h +++ b/kern/semaphore.h @@ -32,7 +32,8 @@ #ifndef _KERN_SEMAPHORE_H #define _KERN_SEMAPHORE_H -#include +#include + #include #include #include diff --git a/kern/semaphore_i.h b/kern/semaphore_i.h index 5498506..acd7cd4 100644 --- a/kern/semaphore_i.h +++ b/kern/semaphore_i.h @@ -18,7 +18,8 @@ #ifndef _KERN_SEMAPHORE_I_H #define _KERN_SEMAPHORE_I_H -#include +#include + #include struct semaphore { diff --git a/kern/sleepq.c b/kern/sleepq.c index 09548db..f5fa672 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -18,11 +18,11 @@ * TODO Analyse hash parameters. */ +#include #include #include #include -#include #include #include #include diff --git a/kern/spinlock.c b/kern/spinlock.c index 3ec36b3..6b9f278 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -53,9 +53,9 @@ * more contention, an operation called downgrading. */ +#include #include -#include #include #include #include diff --git a/kern/spinlock_i.h b/kern/spinlock_i.h index 4501803..c9dcdd1 100644 --- a/kern/spinlock_i.h +++ b/kern/spinlock_i.h @@ -18,10 +18,10 @@ #ifndef _KERN_SPINLOCK_I_H #define _KERN_SPINLOCK_I_H +#include #include #include -#include #include #include #include diff --git a/kern/sref.c b/kern/sref.c index 3f399a3..95e697a 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -41,11 +41,11 @@ * TODO Reconsider whether it's possible to bring back local review queues. */ +#include #include #include #include -#include #include #include #include diff --git a/kern/thread.c b/kern/thread.c index e687558..68aa728 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -81,13 +81,13 @@ * weights in a smoother way than a raw scaling). */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/kern/thread.h b/kern/thread.h index 6270826..e348fe3 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -33,10 +33,10 @@ #ifndef _KERN_THREAD_H #define _KERN_THREAD_H +#include #include #include -#include #include #include #include diff --git a/kern/turnstile.c b/kern/turnstile.c index 7b27d84..78532fb 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -43,11 +43,11 @@ * TODO Analyse hash parameters. */ +#include #include #include #include -#include #include #include #include diff --git a/kern/work.c b/kern/work.c index 714af35..738ab8b 100644 --- a/kern/work.c +++ b/kern/work.c @@ -15,10 +15,10 @@ * along with this program. If not, see . */ +#include #include #include -#include #include #include #include diff --git a/kern/xcall.c b/kern/xcall.c index 364ad0b..ce22aa0 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -15,9 +15,9 @@ * along with this program. If not, see . */ +#include #include -#include #include #include #include diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 8a7272c..a9ba842 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -18,10 +18,10 @@ * TODO Rework so that pmap update errors can be handled. */ +#include #include #include -#include #include #include #include diff --git a/vm/vm_map.c b/vm/vm_map.c index 657f4b5..f506d3b 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -19,11 +19,11 @@ * needed for kernel allocation. */ +#include #include #include #include -#include #include #include #include diff --git a/vm/vm_page.c b/vm/vm_page.c index 284964a..edf213e 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -29,13 +29,13 @@ * The symmetric case is handled likewise. */ +#include #include #include #include #include #include -#include #include #include #include diff --git a/vm/vm_page.h b/vm/vm_page.h index 139f1bf..fa018a0 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -21,10 +21,10 @@ #ifndef _VM_VM_PAGE_H #define _VM_VM_PAGE_H +#include #include #include -#include #include #include #include -- cgit v1.2.3 From 2f4a4c7b6f4e4836c7528f1d0ae2b35d06dd1a23 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 19:23:51 +0200 Subject: Move limits.h to the include directory This turns limits.h into a standard header. --- Makefrag.am | 2 +- include/limits.h | 29 +++++++++++++++++++++++++++++ kern/bitmap.c | 2 +- kern/bitmap.h | 2 +- kern/bitmap_i.h | 3 ++- kern/kmem.c | 2 +- kern/limits.h | 29 ----------------------------- kern/log2.h | 3 +-- kern/rdxtree.c | 2 +- kern/sprintf.c | 2 +- 10 files changed, 38 insertions(+), 38 deletions(-) create mode 100644 include/limits.h delete mode 100644 kern/limits.h diff --git a/Makefrag.am b/Makefrag.am index 0fabdc2..a9f588e 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -5,6 +5,7 @@ EXTRA_DIST += tools/qemu.sh x15_SOURCES += \ include/assert.h \ + include/limits.h \ include/stdio.h \ include/string.h @@ -36,7 +37,6 @@ x15_SOURCES += \ kern/kmem.c \ kern/kmem.h \ kern/kmem_i.h \ - kern/limits.h \ kern/list.h \ kern/list_types.h \ kern/llsync.c \ diff --git a/include/limits.h b/include/limits.h new file mode 100644 index 0000000..2d47e6b --- /dev/null +++ b/include/limits.h @@ -0,0 +1,29 @@ +/* + * 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 3 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 _LIMITS_H +#define _LIMITS_H + +#define CHAR_BIT 8 + +#ifdef __LP64__ +#define LONG_BIT 64 +#else /* __LP64__ */ +#define LONG_BIT 32 +#endif /* __LP64__ */ + +#endif /* _LIMITS_H */ diff --git a/kern/bitmap.c b/kern/bitmap.c index d270b9f..0e13ef5 100644 --- a/kern/bitmap.c +++ b/kern/bitmap.c @@ -15,11 +15,11 @@ * along with this program. If not, see . */ +#include #include #include #include -#include int bitmap_cmp(const unsigned long *a, const unsigned long *b, int nr_bits) diff --git a/kern/bitmap.h b/kern/bitmap.h index a10fb51..a604d17 100644 --- a/kern/bitmap.h +++ b/kern/bitmap.h @@ -24,11 +24,11 @@ #ifndef _KERN_BITMAP_H #define _KERN_BITMAP_H +#include #include #include #include -#include #define BITMAP_DECLARE(name, nr_bits) unsigned long name[BITMAP_LONGS(nr_bits)] diff --git a/kern/bitmap_i.h b/kern/bitmap_i.h index 39a330c..9b79d3c 100644 --- a/kern/bitmap_i.h +++ b/kern/bitmap_i.h @@ -18,7 +18,8 @@ #ifndef _KERN_BITMAP_I_H #define _KERN_BITMAP_I_H -#include +#include + #include #define BITMAP_LONGS(nr_bits) DIV_CEIL(nr_bits, LONG_BIT) diff --git a/kern/kmem.c b/kern/kmem.c index 1f822b8..7c94cb1 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -42,6 +42,7 @@ */ #include +#include #include #include #include @@ -49,7 +50,6 @@ #include #include -#include #include #include #include diff --git a/kern/limits.h b/kern/limits.h deleted file mode 100644 index fa46853..0000000 --- a/kern/limits.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 3 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 _KERN_LIMITS_H -#define _KERN_LIMITS_H - -#define CHAR_BIT 8 - -#ifdef __LP64__ -#define LONG_BIT 64 -#else /* __LP64__ */ -#define LONG_BIT 32 -#endif /* __LP64__ */ - -#endif /* _KERN_LIMITS_H */ diff --git a/kern/log2.h b/kern/log2.h index ebcf2f3..ed12b44 100644 --- a/kern/log2.h +++ b/kern/log2.h @@ -22,8 +22,7 @@ #define _KERN_LOG2_H #include - -#include +#include static inline unsigned int ilog2(unsigned long x) diff --git a/kern/rdxtree.c b/kern/rdxtree.c index b518c7f..9d12439 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -23,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/kern/sprintf.c b/kern/sprintf.c index a606d86..f6346e9 100644 --- a/kern/sprintf.c +++ b/kern/sprintf.c @@ -15,12 +15,12 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include #include /* -- cgit v1.2.3 From a9ce9f79a324e1b8d6d299bc4e43f02f71d0eb7f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 21:10:58 +0200 Subject: kern/error: new ERROR_SRCH macro --- kern/error.c | 2 ++ kern/error.h | 1 + 2 files changed, 3 insertions(+) diff --git a/kern/error.c b/kern/error.c index 02a26d0..a24c5db 100644 --- a/kern/error.c +++ b/kern/error.c @@ -40,6 +40,8 @@ error_str(int error) return "Entry exists"; case ERROR_IO: return "Input/output error"; + case ERROR_SRCH: + return "no such process"; default: return "unknown error"; } diff --git a/kern/error.h b/kern/error.h index 96a2620..4d69a6e 100644 --- a/kern/error.h +++ b/kern/error.h @@ -26,6 +26,7 @@ #define ERROR_NODEV 6 #define ERROR_EXIST 7 #define ERROR_IO 8 +#define ERROR_SRCH 9 /* * Return a string describing the given error. -- cgit v1.2.3 From ccefd921e76ba4ca7f3d6b54691315c0718b039c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 21:11:29 +0200 Subject: kern/shell: separate initialization into two steps The shell must be able to register commands early. As a result, it's now initialized in two steps, one of which starts the shell thread. --- arch/x86/machine/boot.c | 2 ++ kern/kernel.c | 2 +- kern/shell.c | 10 ++++++++-- kern/shell.h | 5 +++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index b7a9b0e..84960eb 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -473,6 +474,7 @@ boot_main(void) uart_bootstrap(); printf_setup(); boot_show_version(); + shell_setup(); arg_info(); uart_info(); pmap_bootstrap(); diff --git a/kern/kernel.c b/kern/kernel.c index 05cf839..1d61b0c 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -50,8 +50,8 @@ kernel_main(void) work_setup(); llsync_setup(); sref_setup(); - shell_setup(); vm_page_info(); + shell_start(); #ifdef X15_RUN_TEST_MODULE test_setup(); diff --git a/kern/shell.c b/kern/shell.c index c4675bf..ba756a5 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -1158,8 +1158,6 @@ shell_run(void *arg) void __init shell_setup(void) { - struct thread_attr attr; - struct thread *thread; unsigned long i; int error; @@ -1169,6 +1167,14 @@ shell_setup(void) error = shell_cmd_register(&shell_default_cmds[i]); error_check(error, "shell_cmd_register"); } +} + +void __init +shell_start(void) +{ + struct thread_attr attr; + struct thread *thread; + int error; thread_attr_init(&attr, THREAD_KERNEL_PREFIX "shell"); thread_attr_set_detached(&attr); diff --git a/kern/shell.h b/kern/shell.h index 5ff4920..a33abbe 100644 --- a/kern/shell.h +++ b/kern/shell.h @@ -55,6 +55,11 @@ void shell_cmd_init(struct shell_cmd *cmd, const char *name, */ void shell_setup(void); +/* + * Start the shell thread. + */ +void shell_start(void); + /* * Register a shell command. * -- cgit v1.2.3 From 60d1eb47786f9b2461067f65f62fde31d34f1089 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 21:12:41 +0200 Subject: kern/{task,thread}: add the task_info and thread_trace shell commands --- kern/task.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/task.h | 42 ++++++++++++++++++++++++++++ kern/thread.c | 59 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/kern/task.c b/kern/task.c index 7f1c53d..66b43bf 100644 --- a/kern/task.c +++ b/kern/task.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -56,20 +57,61 @@ static struct spinlock task_list_lock; static void task_init(struct task *task, const char *name, struct vm_map *map) { + task->nr_refs = 1; spinlock_init(&task->lock); list_init(&task->threads); task->map = map; strlcpy(task->name, name, sizeof(task->name)); } +static void +task_shell_info(int argc, char *argv[]) +{ + struct task *task; + int error; + + if (argc == 1) { + task_info(NULL); + return; + } else { + task = task_lookup(argv[1]); + + if (task == NULL) { + error = ERROR_INVAL; + goto error; + } + + task_info(task); + task_unref(task); + } + + return; + +error: + printf("task: info: %s\n", error_str(error)); +} + +static struct shell_cmd task_shell_cmds[] = { + SHELL_CMD_INITIALIZER("task_info", task_shell_info, + "task_info", "print all tasks and threads"), +}; + void __init task_setup(void) { + unsigned int i; + int error; + kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0); list_init(&task_list); spinlock_init(&task_list_lock); task_init(kernel_task, "x15", kernel_map); list_insert_head(&task_list, &kernel_task->node); + + for (i = 0; i < ARRAY_SIZE(task_shell_cmds); i++) { + error = shell_cmd_register(&task_shell_cmds[i]); + error_check(error, __func__); + } } int @@ -107,6 +149,31 @@ error_task: return error; } +struct task * +task_lookup(const char *name) +{ + struct task *task; + + spinlock_lock(&task_list_lock); + + list_for_each_entry(&task_list, task, node) { + spinlock_lock(&task->lock); + + if (strcmp(task->name, name) == 0) { + task_ref(task); + spinlock_unlock(&task->lock); + spinlock_unlock(&task_list_lock); + return task; + } + + spinlock_unlock(&task->lock); + } + + spinlock_unlock(&task_list_lock); + + return NULL; +} + void task_add_thread(struct task *task, struct thread *thread) { @@ -123,6 +190,26 @@ task_remove_thread(struct task *task, struct thread *thread) spinlock_unlock(&task->lock); } +struct thread * +task_lookup_thread(struct task *task, const char *name) +{ + struct thread *thread; + + spinlock_lock(&task->lock); + + list_for_each_entry(&task->threads, thread, task_node) { + if (strcmp(thread->name, name) == 0) { + thread_ref(thread); + spinlock_unlock(&task->lock); + return thread; + } + } + + spinlock_unlock(&task->lock); + + return NULL; +} + void task_info(struct task *task) { @@ -149,6 +236,8 @@ task_info(struct task *task) * can be used to debug in the middle of most critical sections. * Threads are only destroyed after being removed from their task * so holding the task lock is enough to guarantee existence. + * + * TODO Handle tasks and threads names modifications. */ list_for_each_entry(&task->threads, thread, task_node) { printf(TASK_INFO_ADDR_FMT " %c %8s:" TASK_INFO_ADDR_FMT diff --git a/kern/task.h b/kern/task.h index 6759939..cfe694b 100644 --- a/kern/task.h +++ b/kern/task.h @@ -18,6 +18,7 @@ #ifndef _KERN_TASK_H #define _KERN_TASK_H +#include #include #include #include @@ -32,6 +33,7 @@ * Task structure. */ struct task { + unsigned long nr_refs; struct spinlock lock; struct list node; struct list threads; @@ -44,6 +46,28 @@ struct task { */ extern struct task *kernel_task; +static inline void +task_ref(struct task *task) +{ + unsigned long nr_refs; + + nr_refs = atomic_fetch_add(&task->nr_refs, 1, ATOMIC_RELAXED); + assert(nr_refs != (unsigned long)-1); +} + +static inline void +task_unref(struct task *task) +{ + unsigned long nr_refs; + + nr_refs = atomic_fetch_sub_acq_rel(&task->nr_refs, 1); + assert(nr_refs != 0); + + if (nr_refs == 1) { + /* TODO Task destruction */ + } +} + /* * Initialize the task module. */ @@ -54,6 +78,15 @@ void task_setup(void); */ int task_create(struct task **taskp, const char *name); +/* + * Look up a task from its name. + * + * If a task is found, it gains a reference. Otherwise, NULL is returned. + * + * This function is meant for debugging only. + */ +struct task * task_lookup(const char *name); + /* * Add a thread to a task. */ @@ -64,6 +97,15 @@ void task_add_thread(struct task *task, struct thread *thread); */ void task_remove_thread(struct task *task, struct thread *thread); +/* + * Look up a thread in a task from its name. + * + * If a thread is found, it gains a reference, Otherwise, NULL is returned. + * + * This function is meant for debugging only. + */ +struct thread * task_lookup_thread(struct task *task, const char *name); + /* * Display task information. * diff --git a/kern/thread.c b/kern/thread.c index 68aa728..17ac10b 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -101,6 +101,7 @@ #include #include #include +#include #include #include #include @@ -2228,10 +2229,61 @@ thread_setup_runq(struct thread_runq *runq) thread_setup_idler(runq); } +/* + * This function is meant for debugging only. As a result, it uses a weak + * locking policy which allows tracing threads which state may mutate during + * tracing. + */ +static void +thread_shell_trace(int argc, char *argv[]) +{ + const char *task_name, *thread_name; + struct thread *thread; + struct task *task; + int error; + + if (argc != 3) { + error = ERROR_INVAL; + goto error; + } + + task_name = argv[1]; + thread_name = argv[2]; + + task = task_lookup(task_name); + + if (task == NULL) { + error = ERROR_SRCH; + goto error; + } + + thread = task_lookup_thread(task, thread_name); + task_unref(task); + + if (thread == NULL) { + error = ERROR_SRCH; + goto error; + } + + tcb_trace(&thread->tcb); + thread_unref(thread); + return; + +error: + printf("thread: trace: %s\n", error_str(error)); +} + +static struct shell_cmd thread_shell_cmds[] = { + SHELL_CMD_INITIALIZER("thread_trace", thread_shell_trace, + "thread_trace ", + "print the stack trace of a given thread"), +}; + void __init thread_setup(void) { - int cpu; + unsigned int i; + int cpu, error; for (cpu = 1; (unsigned int)cpu < cpu_count(); cpu++) { thread_bootstrap_common(cpu); @@ -2249,6 +2301,11 @@ thread_setup(void) cpumap_for_each(&thread_active_runqs, cpu) { thread_setup_runq(percpu_ptr(thread_runq, cpu)); } + + for (i = 0; i < ARRAY_SIZE(thread_shell_cmds); i++) { + error = shell_cmd_register(&thread_shell_cmds[i]); + error_check(error, __func__); + } } int -- cgit v1.2.3 From 61f2daf0d20d9293f9b9a31c3a412c6d5d0fe4c4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 21:43:14 +0200 Subject: x86/atkbd: check for the 8042 controller --- arch/x86/machine/atkbd.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index ad66e21..466c1d0 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -502,15 +502,32 @@ atkbd_write(uint8_t data) atkbd_write_data(data); } -static void +static void __init atkbd_flush(void) +{ + uint8_t status; + + atkbd_read(&status, false); +} + +static int __init +atkbd_check(void) { uint8_t status; int error; - do { - error = atkbd_read(&status, false); - } while (!error); + /* Make sure the buffer is empty */ + atkbd_flush(); + + /* Reading should return an error */ + error = atkbd_read(&status, false); + + if (!error) { + printf("atkbd: no keyboard controller\n"); + return ERROR_NODEV; + } + + return 0; } static int __init @@ -521,9 +538,6 @@ atkbd_disable(void) atkbd_write_cmd(ATKBD_CMD_DIS1); atkbd_write_cmd(ATKBD_CMD_DIS2); - - atkbd_flush(); - atkbd_write_cmd(ATKBD_CMD_RDCONF); error = atkbd_read(&byte, true); @@ -733,6 +747,12 @@ atkbd_setup(void) { int error; + error = atkbd_check(); + + if (error) { + return; + } + error = atkbd_disable(); if (error) { -- cgit v1.2.3 From e4d73f8405441ab7a09003773664a16e10b40fed Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 21:55:30 +0200 Subject: x86/atkbd: fix capslock key processing --- arch/x86/machine/atkbd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 466c1d0..26bf9d1 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -657,6 +657,7 @@ atkbd_key_process_ctl(const struct atkbd_key *key) if (!(atkbd_flags & ATKBD_KF_F0)) { atkbd_toggle_capslock(); } + break; default: break; } -- cgit v1.2.3 From 0890cc62c32ad48118bc0731ad07df226fb60431 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 23:51:54 +0200 Subject: kern/console: optimize interrupt handling --- arch/x86/machine/atcons.c | 4 ++-- arch/x86/machine/atcons.h | 2 +- arch/x86/machine/atkbd.c | 5 +---- kern/console.c | 17 ++++++++++++----- kern/console.h | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/arch/x86/machine/atcons.c b/arch/x86/machine/atcons.c index c4c4806..223298c 100644 --- a/arch/x86/machine/atcons.c +++ b/arch/x86/machine/atcons.c @@ -50,7 +50,7 @@ atcons_setup(void) } void -atcons_intr(char c) +atcons_intr(const char *s) { - console_intr(&atcons_console, c); + console_intr(&atcons_console, s); } diff --git a/arch/x86/machine/atcons.h b/arch/x86/machine/atcons.h index 05b870f..ffe22dc 100644 --- a/arch/x86/machine/atcons.h +++ b/arch/x86/machine/atcons.h @@ -39,6 +39,6 @@ void atcons_setup(void); * This function is called by the AT keyboard interrupt handler * to handle machine-independent console management. */ -void atcons_intr(char c); +void atcons_intr(const char *s); #endif /* _X86_ATCONS_H */ diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 26bf9d1..6223d30 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -606,10 +606,7 @@ atkbd_key_process_chars(const struct atkbd_key *key, s = chars[key->id]; if (s != NULL) { - while (*s != '\0') { - atcons_intr(*s); - s++; - } + atcons_intr(s); } } diff --git a/kern/console.c b/kern/console.c index e8c0c28..d7104e4 100644 --- a/kern/console.c +++ b/kern/console.c @@ -136,17 +136,24 @@ console_register(struct console *console) } void -console_intr(struct console *console, char c) +console_intr(struct console *console, const char *s) { assert(!cpu_intr_enabled()); + if (*s == '\0') { + return; + } + spinlock_lock(&console->lock); - if (cbuf_size(&console->recvbuf) == cbuf_capacity(&console->recvbuf)) { - goto out; - } + while (*s != '\0') { + if (cbuf_size(&console->recvbuf) == cbuf_capacity(&console->recvbuf)) { + goto out; + } - cbuf_push(&console->recvbuf, c); + cbuf_push(&console->recvbuf, *s); + s++; + } if ((console->waiter != NULL) && (console->waiter != thread_self())) { thread_wakeup(console->waiter); diff --git a/kern/console.h b/kern/console.h index b967c0d..7455a05 100644 --- a/kern/console.h +++ b/kern/console.h @@ -81,7 +81,7 @@ void console_register(struct console *console); * * Interrupts must be disabled when calling this function. */ -void console_intr(struct console *console, char c); +void console_intr(struct console *console, const char *s); /* * Write/read a single character to all registered console devices. -- cgit v1.2.3 From 2133f7690eeaea94d521a69eba6812f6ddd45f6d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 23:54:50 +0200 Subject: x86/atkbd: fix e0 codes processing --- arch/x86/machine/atkbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 6223d30..36e2ee3 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -708,7 +708,7 @@ atkbd_process_code(uint8_t code) return; } - if (code >= ARRAY_SIZE(atkbd_keys)) { + if (code >= ARRAY_SIZE(atkbd_e0_keys)) { return; } -- cgit v1.2.3 From 5d495c877e78be8dbabdaf631c9c02a72190d6a9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 31 May 2017 23:55:24 +0200 Subject: x86/{atcons,atkbd,cga}: rework escape sequence handling --- arch/x86/machine/atcons.c | 121 +++++++++++++++++++++++++++++++++++++++++++++- arch/x86/machine/atcons.h | 8 +++ arch/x86/machine/atkbd.c | 41 +++++++++++----- arch/x86/machine/cga.c | 15 +++++- arch/x86/machine/cga.h | 6 +++ 5 files changed, 178 insertions(+), 13 deletions(-) diff --git a/arch/x86/machine/atcons.c b/arch/x86/machine/atcons.c index 223298c..e1e14c6 100644 --- a/arch/x86/machine/atcons.c +++ b/arch/x86/machine/atcons.c @@ -15,19 +15,114 @@ * along with this program. If not, see . */ +#include +#include + #include #include #include #include #include +#define ATCONS_ESC_SEQ_MAX_SIZE 8 + static struct console atcons_console; +typedef void (*atcons_esc_seq_fn_t)(void); + +struct atcons_esc_seq { + const char *str; + atcons_esc_seq_fn_t fn; +}; + +static bool atcons_escape; +static char atcons_esc_seq[ATCONS_ESC_SEQ_MAX_SIZE]; +static unsigned int atcons_esc_seq_index; + +static void +atcons_process_left(void) +{ + cga_cursor_left(); +} + +static void +atcons_process_right(void) +{ + cga_cursor_right(); +} + +static const struct atcons_esc_seq atcons_esc_seqs[] = { + { "[1D", atcons_process_left }, + { "[1C", atcons_process_right }, +}; + +static void +atcons_reset_esc_seq(void) +{ + atcons_escape = false; +} + +static int +atcons_esc_seq_lookup(const struct atcons_esc_seq **seqp) +{ + const struct atcons_esc_seq *seq; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(atcons_esc_seqs); i++) { + seq = &atcons_esc_seqs[i]; + + if (strncmp(seq->str, atcons_esc_seq, atcons_esc_seq_index) == 0) { + if (strlen(seq->str) == atcons_esc_seq_index) { + *seqp = seq; + return 0; + } else { + return ERROR_AGAIN; + } + } + } + + return ERROR_SRCH; +} + +static void +atcons_process_esc_seq(char c) +{ + const struct atcons_esc_seq *seq; + int error; + + if (atcons_esc_seq_index >= sizeof(atcons_esc_seq)) { + atcons_reset_esc_seq(); + return; + } + + atcons_esc_seq[atcons_esc_seq_index] = c; + atcons_esc_seq_index++; + + error = atcons_esc_seq_lookup(&seq); + + if (error) { + if (error != ERROR_AGAIN) { + atcons_reset_esc_seq(); + } + } else { + seq->fn(); + atcons_reset_esc_seq(); + } +} + static void atcons_putc(struct console *console, char c) { (void)console; - cga_putc(c); + + if (c == '\e') { + atcons_escape = true; + atcons_esc_seq_index = 0; + } else if (atcons_escape) { + atcons_process_esc_seq(c); + } else { + cga_putc(c); + } } static const struct console_ops atcons_ops = { @@ -54,3 +149,27 @@ atcons_intr(const char *s) { console_intr(&atcons_console, s); } + +void +atcons_left(void) +{ + atcons_intr("\e[D"); +} + +void +atcons_bottom(void) +{ + atcons_intr("\e[B"); +} + +void +atcons_right(void) +{ + atcons_intr("\e[C"); +} + +void +atcons_up(void) +{ + atcons_intr("\e[A"); +} diff --git a/arch/x86/machine/atcons.h b/arch/x86/machine/atcons.h index ffe22dc..d6923df 100644 --- a/arch/x86/machine/atcons.h +++ b/arch/x86/machine/atcons.h @@ -41,4 +41,12 @@ void atcons_setup(void); */ void atcons_intr(const char *s); +/* + * Direction control processing functions. + */ +void atcons_left(void); +void atcons_bottom(void); +void atcons_right(void); +void atcons_up(void); + #endif /* _X86_ATCONS_H */ diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 36e2ee3..c5140c4 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -248,10 +248,10 @@ static const struct atkbd_key atkbd_e0_keys[] = { [0x7d] = { 0, ATKBD_KEY_PGUP }, [0x7a] = { 0, ATKBD_KEY_PGDOWN }, - [0x6b] = { 0, ATKBD_KEY_LEFT }, - [0x72] = { 0, ATKBD_KEY_BOTTOM }, - [0x74] = { 0, ATKBD_KEY_RIGHT }, - [0x75] = { 0, ATKBD_KEY_UP }, + [0x6b] = { ATKBD_KM_CTL, ATKBD_KEY_LEFT }, + [0x72] = { ATKBD_KM_CTL, ATKBD_KEY_BOTTOM }, + [0x74] = { ATKBD_KM_CTL, ATKBD_KEY_RIGHT }, + [0x75] = { ATKBD_KM_CTL, ATKBD_KEY_UP }, [0x4a] = { 0, ATKBD_KEY_KP_SLASH }, [0x5a] = { 0, ATKBD_KEY_KP_ENTER }, @@ -315,11 +315,6 @@ static const char *atkbd_chars[] = { [ATKBD_KEY_DELETE] = "\e[3~", - [ATKBD_KEY_LEFT] = "\e[D", - [ATKBD_KEY_BOTTOM] = "\e[B", - [ATKBD_KEY_RIGHT] = "\e[C", - [ATKBD_KEY_UP] = "\e[A", - [ATKBD_KEY_KP_SLASH] = "/", [ATKBD_KEY_KP_STAR] = "*", [ATKBD_KEY_KP_MINUS] = "-", @@ -654,6 +649,30 @@ atkbd_key_process_ctl(const struct atkbd_key *key) if (!(atkbd_flags & ATKBD_KF_F0)) { atkbd_toggle_capslock(); } + break; + case ATKBD_KEY_LEFT: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atcons_left(); + } + + break; + case ATKBD_KEY_BOTTOM: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atcons_bottom(); + } + + break; + case ATKBD_KEY_RIGHT: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atcons_right(); + } + + break; + case ATKBD_KEY_UP: + if (!(atkbd_flags & ATKBD_KF_F0)) { + atcons_up(); + } + break; default: break; @@ -689,7 +708,7 @@ atkbd_process_e0_code(uint8_t code) return; } - if (code >= ARRAY_SIZE(atkbd_keys)) { + if (code >= ARRAY_SIZE(atkbd_e0_keys)) { return; } @@ -708,7 +727,7 @@ atkbd_process_code(uint8_t code) return; } - if (code >= ARRAY_SIZE(atkbd_e0_keys)) { + if (code >= ARRAY_SIZE(atkbd_keys)) { return; } diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 1ec8035..1d1cc4b 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -133,7 +133,6 @@ cga_putc(char c) } else if (c == '\b') { if (cga_cursor > 0) { cga_cursor--; - ((uint16_t *)cga_memory)[cga_cursor] = CGA_BLANK; cga_set_cursor_position(cga_cursor); } } else if (c == '\t') { @@ -181,3 +180,17 @@ cga_setup(void) cga_cursor = cga_get_cursor_position(); } + +void +cga_cursor_left(void) +{ + cga_cursor--; + cga_set_cursor_position(cga_cursor); +} + +void +cga_cursor_right(void) +{ + cga_cursor++; + cga_set_cursor_position(cga_cursor); +} diff --git a/arch/x86/machine/cga.h b/arch/x86/machine/cga.h index f5a5d28..2310bc9 100644 --- a/arch/x86/machine/cga.h +++ b/arch/x86/machine/cga.h @@ -31,4 +31,10 @@ void cga_setup(void); */ void cga_putc(char c); +/* + * Cursor control functions. + */ +void cga_cursor_left(void); +void cga_cursor_right(void); + #endif /* _X86_CGA_H */ -- cgit v1.2.3 From 0c5308c10ab34dc50fd22f39bb068cc145c658f2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 1 Jun 2017 00:04:51 +0200 Subject: x86/uart: update call to console_intr --- arch/x86/machine/uart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 24fcace..a4a01cb 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -132,6 +132,9 @@ static void uart_recv_intr(struct uart *uart) { uint8_t byte; + char tmp[2]; + + tmp[1] = '\0'; for (;;) { byte = uart_read(uart, UART_REG_LSR); @@ -141,7 +144,8 @@ uart_recv_intr(struct uart *uart) } byte = uart_read(uart, UART_REG_DAT); - console_intr(&uart->console, (char)byte); + tmp[0] = (char)byte; + console_intr(&uart->console, tmp); } } -- cgit v1.2.3 From 4fc4e55bf7840115075200ba51f5deddc9ce52fc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 1 Jun 2017 01:02:35 +0200 Subject: kern/error: new ERROR_TIMEDOUT macro --- kern/error.c | 2 ++ kern/error.h | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/kern/error.c b/kern/error.c index a24c5db..83aef26 100644 --- a/kern/error.c +++ b/kern/error.c @@ -42,6 +42,8 @@ error_str(int error) return "Input/output error"; case ERROR_SRCH: return "no such process"; + case ERROR_TIMEDOUT: + return "timeout error"; default: return "unknown error"; } diff --git a/kern/error.h b/kern/error.h index 4d69a6e..a77db97 100644 --- a/kern/error.h +++ b/kern/error.h @@ -18,15 +18,16 @@ #ifndef _KERN_ERROR_H #define _KERN_ERROR_H -#define ERROR_NOMEM 1 -#define ERROR_AGAIN 2 -#define ERROR_INVAL 3 -#define ERROR_BUSY 4 -#define ERROR_FAULT 5 -#define ERROR_NODEV 6 -#define ERROR_EXIST 7 -#define ERROR_IO 8 -#define ERROR_SRCH 9 +#define ERROR_NOMEM 1 +#define ERROR_AGAIN 2 +#define ERROR_INVAL 3 +#define ERROR_BUSY 4 +#define ERROR_FAULT 5 +#define ERROR_NODEV 6 +#define ERROR_EXIST 7 +#define ERROR_IO 8 +#define ERROR_SRCH 9 +#define ERROR_TIMEDOUT 10 /* * Return a string describing the given error. -- cgit v1.2.3 From c709460baab317d1181ed9183491e586528996ea Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 1 Jun 2017 01:03:15 +0200 Subject: x86/atkbd: improve error handling and controller detection --- arch/x86/machine/atkbd.c | 119 ++++++++++++++++++++++++++--------------------- 1 file changed, 67 insertions(+), 52 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index c5140c4..723c4cd 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -40,7 +40,10 @@ #define ATKBD_STATUS_OUT_FULL 0x01 #define ATKBD_STATUS_IN_FULL 0x02 +#define ATKBD_STATUS_TIMEOUT_ERROR 0x40 #define ATKBD_STATUS_PARITY_ERROR 0x80 +#define ATKBD_STATUS_ERROR (ATKBD_STATUS_PARITY_ERROR \ + | ATKBD_STATUS_TIMEOUT_ERROR) #define ATKBD_CMD_RDCONF 0x20 #define ATKBD_CMD_WRCONF 0x60 @@ -419,10 +422,33 @@ atkbd_write_data(uint8_t data) io_write_byte(ATKBD_PORT_DATA, data); } -static uint8_t -atkbd_read_status(void) +static int +atkbd_read_status(bool check_out) { - return io_read_byte(ATKBD_PORT_STATUS); + uint8_t status; + + status = io_read_byte(ATKBD_PORT_STATUS); + + /* + * XXX This case is rare enough that it can be used to identify the lack + * of hardware. + */ + if (status == 0xff) { + printf("atkbd: no keyboard controller\n"); + return ERROR_NODEV; + } else if (status & ATKBD_STATUS_PARITY_ERROR) { + printf("atkbd: parity error\n"); + return ERROR_IO; + } else if (status & ATKBD_STATUS_TIMEOUT_ERROR) { + printf("atkbd: timeout error\n"); + return ERROR_TIMEDOUT; + } + + if (check_out) { + return (status & ATKBD_STATUS_OUT_FULL) ? 0 : ERROR_AGAIN; + } else { + return (status & ATKBD_STATUS_IN_FULL) ? ERROR_AGAIN : 0; + } } static void @@ -434,95 +460,84 @@ atkbd_write_cmd(uint8_t cmd) static int atkbd_out_wait(void) { - uint8_t status; + int error; for (;;) { - status = atkbd_read_status(); + error = atkbd_read_status(true); - if (status & ATKBD_STATUS_OUT_FULL) { + if (error != ERROR_AGAIN) { break; } } - if (status & ATKBD_STATUS_PARITY_ERROR) { - printf("atkbd: parity error\n"); - return ERROR_IO; - } - - return 0; + return error; } -static void +static int atkbd_in_wait(void) { - uint8_t status; + int error; for (;;) { - status = atkbd_read_status(); + error = atkbd_read_status(false); - if (!(status & ATKBD_STATUS_IN_FULL)) { + if (error != ERROR_AGAIN) { break; } } + + return error; } static int atkbd_read(uint8_t *datap, bool wait) { - uint8_t status; int error; if (wait) { error = atkbd_out_wait(); - - if (error) { - return error; - } } else { - status = atkbd_read_status(); + error = atkbd_read_status(true); + } - if (!(status & ATKBD_STATUS_OUT_FULL)) { - return ERROR_AGAIN; - } + if (error) { + return error; } *datap = atkbd_read_data(); return 0; } -static void +static int atkbd_write(uint8_t data) { - atkbd_in_wait(); - atkbd_write_data(data); -} + int error; -static void __init -atkbd_flush(void) -{ - uint8_t status; + error = atkbd_in_wait(); + + if (error) { + return error; + } - atkbd_read(&status, false); + atkbd_write_data(data); + return 0; } static int __init -atkbd_check(void) +atkbd_flush(void) { - uint8_t status; + uint8_t byte; int error; - /* Make sure the buffer is empty */ - atkbd_flush(); - - /* Reading should return an error */ - error = atkbd_read(&status, false); + do { + error = atkbd_read(&byte, false); + } while (!error); - if (!error) { - printf("atkbd: no keyboard controller\n"); - return ERROR_NODEV; + if (error == ERROR_AGAIN) { + error = 0; } - return 0; + return error; } static int __init @@ -542,9 +557,7 @@ atkbd_disable(void) byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2 | ATKBD_CONF_ENINT1); atkbd_write_cmd(ATKBD_CMD_WRCONF); - atkbd_write(byte); - - return 0; + return atkbd_write(byte); } static int __init @@ -565,11 +578,13 @@ atkbd_enable(void) byte &= ~(ATKBD_CONF_ENTRANS | ATKBD_CONF_ENINT2); byte |= ATKBD_CONF_ENINT1; atkbd_write_cmd(ATKBD_CMD_WRCONF); - atkbd_write(byte); + error = atkbd_write(byte); - atkbd_flush(); + if (error) { + return error; + } - return 0; + return atkbd_flush(); } static void @@ -764,7 +779,7 @@ atkbd_setup(void) { int error; - error = atkbd_check(); + error = atkbd_flush(); if (error) { return; -- cgit v1.2.3 From c30a5174959ad0e5914a4d149d862b1fa8e4dcc4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 19:55:30 +0200 Subject: x86/atkbd: add comments --- arch/x86/machine/atkbd.c | 69 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 723c4cd..1ed4c86 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -34,10 +34,16 @@ #include #include +/* + * Intel 8042 I/O ports. + */ #define ATKBD_PORT_DATA 0x60 #define ATKBD_PORT_STATUS 0x64 #define ATKBD_PORT_CMD 0x64 +/* + * Status register bits. + */ #define ATKBD_STATUS_OUT_FULL 0x01 #define ATKBD_STATUS_IN_FULL 0x02 #define ATKBD_STATUS_TIMEOUT_ERROR 0x40 @@ -45,6 +51,9 @@ #define ATKBD_STATUS_ERROR (ATKBD_STATUS_PARITY_ERROR \ | ATKBD_STATUS_TIMEOUT_ERROR) +/* + * Controller commands. + */ #define ATKBD_CMD_RDCONF 0x20 #define ATKBD_CMD_WRCONF 0x60 #define ATKBD_CMD_DIS2 0xa7 @@ -52,12 +61,26 @@ #define ATKBD_CMD_DIS1 0xad #define ATKBD_CMD_EN1 0xae +/* + * Controller configuration register bits. + */ #define ATKBD_CONF_ENINT1 0x01 #define ATKBD_CONF_ENINT2 0x02 #define ATKBD_CONF_ENTRANS 0x40 +/* + * Controller interrupt. + * + * This driver only supports keyboard input and doesn't use interrupt 12. + */ #define ATKBD_INTR1 1 +/* + * Key identifiers. + * + * Keys are obtained from static sparse tables, where undefined entries are + * initialized to 0. As a result, make that value an invalid key. + */ enum atkbd_key_id { ATKBD_KEY_INVALID = 0, ATKBD_KEY_1 = 1, @@ -153,6 +176,9 @@ enum atkbd_key_id { ATKBD_KEY_KP_DEL, }; +/* + * Key modifiers. + */ #define ATKBD_KM_SHIFT 0x01 /* Shift / caps lock modifier applies */ #define ATKBD_KM_KP 0x02 /* Num lock modifier applies */ #define ATKBD_KM_CTL 0x04 /* Unmodified key is a control character */ @@ -162,6 +188,14 @@ struct atkbd_key { enum atkbd_key_id id; }; +/* + * Regular key table. + * + * Keys are indexed by their "scan code set 2" code. Undefined entries are + * initialized to 0, which is a known invalid identifier. Modifiers are used + * to select a different key->characters table, or key-specific processing + * in the case of control keys. + */ static const struct atkbd_key atkbd_keys[] = { [0x16] = { ATKBD_KM_SHIFT, ATKBD_KEY_1 }, [0x1e] = { ATKBD_KM_SHIFT, ATKBD_KEY_2 }, @@ -240,6 +274,12 @@ static const struct atkbd_key atkbd_keys[] = { [0x71] = { ATKBD_KM_KP, ATKBD_KEY_KP_DEL }, }; +/* + * Key table for e0 codes. + * + * This table is similar to the regular key table, except it is used if an + * e0 code was read in the input byte sequence. + */ static const struct atkbd_key atkbd_e0_keys[] = { [0x11] = { ATKBD_KM_CTL, ATKBD_KEY_ALTGR }, [0x14] = { ATKBD_KM_CTL, ATKBD_KEY_RCTRL }, @@ -260,6 +300,12 @@ static const struct atkbd_key atkbd_e0_keys[] = { [0x5a] = { 0, ATKBD_KEY_KP_ENTER }, }; +/* + * Key to characters table. + * + * This table is used for keys with no modifiers or if none of the modifiers + * apply at input time. + */ static const char *atkbd_chars[] = { [ATKBD_KEY_1] = "1", [ATKBD_KEY_2] = "2", @@ -327,6 +373,9 @@ static const char *atkbd_chars[] = { [ATKBD_KEY_KP_DEL] = "\e[3~", }; +/* + * Key to characters table when the shift modifier applies. + */ static const char *atkbd_shift_chars[] = { [ATKBD_KEY_1] = "!", [ATKBD_KEY_2] = "@", @@ -379,6 +428,9 @@ static const char *atkbd_shift_chars[] = { [ATKBD_KEY_SLASH] = "?", }; +/* + * Key to keypad characters table when the numlock modifier applies. + */ static const char *atkbd_kp_chars[] = { [ATKBD_KEY_KP_HOME] = "7", [ATKBD_KEY_KP_UP] = "8", @@ -392,13 +444,16 @@ static const char *atkbd_kp_chars[] = { [ATKBD_KEY_KP_DEL] = ".", }; -#define ATKBD_KF_E0 0x01 -#define ATKBD_KF_F0 0x02 -#define ATKBD_KF_LSHIFT 0x04 -#define ATKBD_KF_RSHIFT 0x08 -#define ATKBD_KF_NUMLOCK 0x10 -#define ATKBD_KF_CAPSLOCK 0x20 -#define ATKBD_KF_SCROLLLOCK 0x40 +/* + * Global flags. + */ +#define ATKBD_KF_E0 0x01 /* Current sequence includes an 0xe0 byte */ +#define ATKBD_KF_F0 0x02 /* Current sequence includes an 0xf0 byte, + which usually indicates a key release */ +#define ATKBD_KF_LSHIFT 0x04 /* Left shift key is being pressed */ +#define ATKBD_KF_RSHIFT 0x08 /* Right shift key is being pressed */ +#define ATKBD_KF_NUMLOCK 0x10 /* Numlock key has been toggled on */ +#define ATKBD_KF_CAPSLOCK 0x20 /* Caps lock key has been toggled on */ #define ATKBD_KF_SHIFT (ATKBD_KF_CAPSLOCK \ | ATKBD_KF_RSHIFT \ -- cgit v1.2.3 From 5df39a5a2976cd9a4849cfc01f73c4fcc5bbdf46 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 19:56:31 +0200 Subject: kern/error: fix error strings case --- kern/error.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kern/error.c b/kern/error.c index 83aef26..110c804 100644 --- a/kern/error.c +++ b/kern/error.c @@ -33,13 +33,13 @@ error_str(int error) case ERROR_BUSY: return "device or resource busy"; case ERROR_FAULT: - return "Bad address"; + return "bad address"; case ERROR_NODEV: - return "No such device"; + return "no such device"; case ERROR_EXIST: - return "Entry exists"; + return "entry exists"; case ERROR_IO: - return "Input/output error"; + return "input/output error"; case ERROR_SRCH: return "no such process"; case ERROR_TIMEDOUT: -- cgit v1.2.3 From f8f72fe735799b0f464c1036513aacd8a442af6e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 20:16:01 +0200 Subject: kern/shell: add comments --- kern/shell.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/kern/shell.c b/kern/shell.c index ba756a5..38f2991 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -224,6 +224,10 @@ shell_cmd_lookup(const char *name) } /* + * Look up the first command that matches a given string. + * + * The input string is defined by the given string pointer and size. + * * The global lock must be acquired before calling this function. */ static const struct shell_cmd * @@ -242,6 +246,21 @@ shell_cmd_match(const struct shell_cmd *cmd, const char *str, } /* + * Attempt command auto-completion. + * + * The given string is the beginning of a command, or the empty string. + * The sizep parameter initially points to the size of the given string. + * If the string matches any registered command, the cmdp pointer is + * updated to point to the first matching command in the sorted list of + * commands, and sizep is updated to the number of characters in the + * command name that are common in subsequent commands. The command + * pointer and the returned size can be used to print a list of commands + * eligible for completion. + * + * If there is a single match for the given string, return 0. If there + * are more than one match, return ERROR_AGAIN. If there is no match, + * return ERROR_INVAL. + * * The global lock must be acquired before calling this function. */ static int @@ -309,6 +328,10 @@ shell_cmd_complete(const char *str, unsigned long *sizep, } /* + * Print a list of commands eligible for completion, starting at the + * given command. Other eligible commands share the same prefix, as + * defined by the size argument. + * * The global lock must be acquired before calling this function. */ static void @@ -811,6 +834,10 @@ shell_process_raw_char(char c) goto out; } + /* + * This assumes that the backspace character only moves the cursor + * without erasing characters. + */ printf("%s", shell_line_str(current_line) + shell_cursor - 1); remaining_chars = shell_line_size(current_line) - shell_cursor; -- cgit v1.2.3 From fa70af2fcc24f8e5aa88a37407d30ca24cc8bf2c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 20:33:39 +0200 Subject: kern/atomic: update shortcut macros --- kern/atomic.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kern/atomic.h b/kern/atomic.h index 75b0701..63f0ac7 100644 --- a/kern/atomic.h +++ b/kern/atomic.h @@ -116,6 +116,9 @@ MACRO_END * Common shortcuts. */ +#define atomic_load_acquire(ptr) atomic_load(ptr, ATOMIC_ACQUIRE) +#define atomic_store_release(ptr, val) atomic_store(ptr, val, ATOMIC_RELEASE) + #define atomic_cas_acquire(ptr, oval, nval) \ atomic_cas(ptr, oval, nval, ATOMIC_ACQUIRE) @@ -129,9 +132,6 @@ MACRO_END #define atomic_swap_release(ptr, val) atomic_swap(ptr, val, ATOMIC_RELEASE) #define atomic_swap_acq_rel(ptr, val) atomic_swap(ptr, val, ATOMIC_ACQ_REL) -#define atomic_fetch_add_acq_rel(ptr, val) \ - atomic_fetch_add(ptr, val, ATOMIC_ACQ_REL) - #define atomic_fetch_sub_acq_rel(ptr, val) \ atomic_fetch_sub(ptr, val, ATOMIC_ACQ_REL) -- cgit v1.2.3 From d9d5380bd7b1d6bcc8e8c3fb64da9870008de593 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 21:59:30 +0200 Subject: kern/spinlock: use new atomic accessor macros --- kern/spinlock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kern/spinlock.c b/kern/spinlock.c index 6b9f278..aa49cff 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -204,7 +204,7 @@ spinlock_load_first_qid(const struct spinlock *lock) { unsigned int value; - value = atomic_load(&lock->value, ATOMIC_ACQUIRE); + value = atomic_load_acquire(&lock->value); return (value >> SPINLOCK_QID_MAX_BITS) & SPINLOCK_QID_MASK; } @@ -268,7 +268,7 @@ spinlock_lock_slow(struct spinlock *lock) } for (;;) { - locked = atomic_load(&qnode->locked, ATOMIC_ACQUIRE); + locked = atomic_load_acquire(&qnode->locked); if (!locked) { break; @@ -316,5 +316,5 @@ spinlock_unlock_slow(struct spinlock *lock) } next_qnode = spinlock_get_remote_qnode(next_qid); - atomic_store(&next_qnode->locked, false, ATOMIC_RELEASE); + atomic_store_release(&next_qnode->locked, false); } -- cgit v1.2.3 From f4b232fdc9318a6c8275b35d3201eae5b2e481dd Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 21:59:54 +0200 Subject: kern/spinlock: describe memory order constraints --- kern/spinlock.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kern/spinlock.c b/kern/spinlock.c index aa49cff..68e8421 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -51,6 +51,16 @@ * and fast paths operations fail. The lock operation must make sure * that the lock value is restored to SPINLOCK_LOCKED if there is no * more contention, an operation called downgrading. + * + * In order to enforce correct visibility of the critical section, + * acquire and release atomic operations are used when accessing a + * qnode lock. Similarly, acquire and release semantics are also used + * when accessing the lock word which stores the first and last QIDs, + * so that the memory operations on the qnodes referenced by those QIDs + * are correctly enforced. Accessing the next QID in a qnode however + * is performed with no memory order constraint, because the qnodes + * referenced by those QID are never accessed unless they are the first + * or the last, cases which do enforce ordering. */ #include -- cgit v1.2.3 From 8a21903a9f19941bf40188ca646298ef2445fa80 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 22:05:38 +0200 Subject: x86/trap: remove TODO --- arch/x86/machine/trap_asm.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/trap_asm.S b/arch/x86/machine/trap_asm.S index a60bbf0..2f683bd 100644 --- a/arch/x86/machine/trap_asm.S +++ b/arch/x86/machine/trap_asm.S @@ -81,7 +81,7 @@ ASM_ENTRY(trap_common) call trap_get_interrupt_stack testq %rax, %rax /* switch stack ? */ jz 1f - movq %rax, %rsp /* switch to interrupt stack TODO conditional move ? */ + movq %rax, %rsp /* switch to interrupt stack ? */ 1: xorq %rbp, %rbp /* block stack tracing */ movq %rbx, %rdi -- cgit v1.2.3 From 77bb91faa0059b157220ef2e1732bdf89618a375 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 2 Jun 2017 22:57:36 +0200 Subject: x86/tcb: add comments --- arch/x86/machine/cpu.h | 2 +- arch/x86/machine/tcb_asm.S | 90 +++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index b384e55..4e4ccbb 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -46,7 +46,7 @@ /* * EFLAGS register flags. */ -#define CPU_EFL_ONE 0x00000002 +#define CPU_EFL_ONE 0x00000002 /* Reserved, must be one */ #define CPU_EFL_IF 0x00000200 /* diff --git a/arch/x86/machine/tcb_asm.S b/arch/x86/machine/tcb_asm.S index 2089e93..e4e2c45 100644 --- a/arch/x86/machine/tcb_asm.S +++ b/arch/x86/machine/tcb_asm.S @@ -24,24 +24,24 @@ #ifdef __LP64__ ASM_ENTRY(tcb_context_load) - movq (%rdi), %rbp - movq 8(%rdi), %rsp - movq 16(%rdi), %rax - pushq $CPU_EFL_ONE - popfq - jmp *%rax + movq (%rdi), %rbp /* load frame pointer from TCB */ + movq 8(%rdi), %rsp /* load stack pointer from TCB */ + movq 16(%rdi), %rax /* load instruction pointer from TCB */ + pushq $CPU_EFL_ONE /* prepare new RFLAGS register value */ + popfq /* load value into RFLAGS register */ + jmp *%rax /* branch to loaded instruction pointer */ ASM_END(tcb_context_load) #else /* __LP64__ */ ASM_ENTRY(tcb_context_load) - movl 4(%esp), %eax - movl (%eax), %ebp - movl 4(%eax), %esp - movl 8(%eax), %ecx - pushl $CPU_EFL_ONE - popfl - jmp *%ecx + movl 4(%esp), %eax /* load TCB address */ + movl (%eax), %ebp /* load frame pointer from TCB */ + movl 4(%eax), %esp /* load stack pointer from TCB */ + movl 8(%eax), %ecx /* load instruction pointer from TCB */ + pushl $CPU_EFL_ONE /* prepare new EFLAGS register value */ + popfl /* load value into EFLAGS register */ + jmp *%ecx /* branch to loaded instruction pointer */ ASM_END(tcb_context_load) #endif /* __LP64__ */ @@ -51,28 +51,36 @@ ASM_END(tcb_context_load) #ifdef __LP64__ ASM_ENTRY(tcb_start) - popq %rax - call *%rax + popq %rax /* load function passed at TCB initialization (this + makes the stack pointer reach the stack top) */ + call *%rax /* branch to loaded function, pushing the return + address to start a clean stack trace */ /* Never reached */ - nop + nop /* Make the return address point to an instruction + inside the function to build a clean stack trace */ ASM_END(tcb_start) ASM_ENTRY(tcb_context_switch) - pushfq + pushfq /* store registers as required by ABI */ pushq %rbx pushq %r12 pushq %r13 pushq %r14 pushq %r15 - movq %rbp, (%rdi) - movq %rsp, 8(%rdi) - movq $1f, 16(%rdi) - movq (%rsi), %rbp - movq 8(%rsi), %rsp - movq 16(%rsi), %rax - jmp *%rax + movq %rbp, (%rdi) /* store frame pointer into prev TCB */ + movq %rsp, 8(%rdi) /* store stack pointer into prev TCB */ + movq $1f, 16(%rdi) /* store next instruction address into prev TCB */ + movq (%rsi), %rbp /* load frame pointer from next TCB */ + movq 8(%rsi), %rsp /* load stack pointer from next TCB */ + movq 16(%rsi), %rax /* load instruction pointer from next TCB */ + jmp *%rax /* branch to loaded instruction pointer */ +/* + * This code is run on context restoration. The frame and stack pointers + * have already been loaded to their correct values. Load registers which + * were stored on the stack when the context was saved and return. + */ 1: popq %r15 popq %r14 @@ -86,28 +94,36 @@ ASM_END(tcb_context_switch) #else /* __LP64__ */ ASM_ENTRY(tcb_start) - popl %eax - call *%eax + popl %eax /* load function passed at TCB initialization (this + makes the stack pointer reach the stack top) */ + call *%eax /* branch to loaded function, pushing the return + address to start a clean stack trace */ /* Never reached */ - nop + nop /* Make the return address point to an instruction + inside the function to build a clean stack trace */ ASM_END(tcb_start) ASM_ENTRY(tcb_context_switch) - movl 4(%esp), %eax - movl 8(%esp), %ecx - pushfl + movl 4(%esp), %eax /* load prev TCB address */ + movl 8(%esp), %ecx /* load next TCB address */ + pushfl /* store registers as required by ABI */ pushl %ebx pushl %edi pushl %esi - movl %ebp, (%eax) - movl %esp, 4(%eax) - movl $1f, 8(%eax) - movl (%ecx), %ebp - movl 4(%ecx), %esp - movl 8(%ecx), %edx - jmp *%edx + movl %ebp, (%eax) /* store frame pointer into prev TCB */ + movl %esp, 4(%eax) /* store stack pointer into prev TCB */ + movl $1f, 8(%eax) /* store next instruction address into prev TCB */ + movl (%ecx), %ebp /* load frame pointer from next TCB */ + movl 4(%ecx), %esp /* load stack pointer from next TCB */ + movl 8(%ecx), %edx /* load instruction pointer from next TCB */ + jmp *%edx /* branch to loaded instruction pointer */ +/* + * This code is run on context restoration. The frame and stack pointers + * have already been loaded to their correct values. Load registers which + * were stored on the stack when the context was saved and return. + */ 1: popl %esi popl %edi -- cgit v1.2.3 From 1b902346ce502870329bda19d92d317a21cab56e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 14:20:13 +0200 Subject: kern/thread: improve thread_wakeup robustness --- kern/console.c | 4 +--- kern/sref.c | 3 +-- kern/thread.c | 4 ++++ kern/thread.h | 3 ++- kern/work.c | 4 +--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/kern/console.c b/kern/console.c index d7104e4..fef4f65 100644 --- a/kern/console.c +++ b/kern/console.c @@ -155,9 +155,7 @@ console_intr(struct console *console, const char *s) s++; } - if ((console->waiter != NULL) && (console->waiter != thread_self())) { - thread_wakeup(console->waiter); - } + thread_wakeup(console->waiter); out: spinlock_unlock(&console->lock); diff --git a/kern/sref.c b/kern/sref.c index 95e697a..9c2d776 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -976,8 +976,7 @@ sref_report_periodic_event(void) cache = sref_cache_get(); - if (!sref_cache_is_registered(cache) - || (cache->manager == thread_self())) { + if (!sref_cache_is_registered(cache)) { return; } diff --git a/kern/thread.c b/kern/thread.c index 17ac10b..25d8177 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2452,6 +2452,10 @@ thread_wakeup(struct thread *thread) struct thread_runq *runq; unsigned long flags; + if ((thread == NULL) || (thread == thread_self())) { + return; + } + /* * There is at most one reference on threads that were never dispatched, * in which case there is no need to lock anything. diff --git a/kern/thread.h b/kern/thread.h index e348fe3..144735d 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -219,7 +219,8 @@ void thread_sleep(struct spinlock *interlock, const void *wchan_addr, /* * Schedule a thread for execution on a processor. * - * No action is performed if the target thread is already in the running state. + * No action is performed if the target thread is NULL, the calling thread, + * or already in the running state. */ void thread_wakeup(struct thread *thread); diff --git a/kern/work.c b/kern/work.c index 738ab8b..6c0ed62 100644 --- a/kern/work.c +++ b/kern/work.c @@ -259,9 +259,7 @@ work_pool_wakeup_manager(struct work_pool *pool) return; } - if ((pool->manager != NULL) && (pool->manager->thread != thread_self())) { - thread_wakeup(pool->manager->thread); - } + thread_wakeup(pool->manager->thread); } static void -- cgit v1.2.3 From 3325ff8fec93d7b2fa2f8359aff8de51b406d552 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 15:35:17 +0200 Subject: x86/acpimp: rename module to acpi This module is likely to do more than provide multi-processor information. --- arch/x86/Makefrag.am | 4 +- arch/x86/machine/acpi.c | 538 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/acpi.h | 33 +++ arch/x86/machine/acpimp.c | 538 ---------------------------------------------- arch/x86/machine/acpimp.h | 33 --- arch/x86/machine/cpu.c | 4 +- 6 files changed, 575 insertions(+), 575 deletions(-) create mode 100644 arch/x86/machine/acpi.c create mode 100644 arch/x86/machine/acpi.h delete mode 100644 arch/x86/machine/acpimp.c delete mode 100644 arch/x86/machine/acpimp.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 0971530..4ec1f7a 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -24,8 +24,8 @@ x15_LDFLAGS += -m64 endif AMD64 x15_SOURCES += \ - arch/x86/machine/acpimp.c \ - arch/x86/machine/acpimp.h \ + arch/x86/machine/acpi.c \ + arch/x86/machine/acpi.h \ arch/x86/machine/atcons.c \ arch/x86/machine/atcons.h \ arch/x86/machine/atkbd.c \ diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c new file mode 100644 index 0000000..5927e95 --- /dev/null +++ b/arch/x86/machine/acpi.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2012-2017 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 3 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 +#include +#include +#include +#include + +/* + * Alignment of the RSDP. + */ +#define ACPI_RSDP_ALIGN 16 + +/* + * Signature of the root system description pointer. + */ +#define ACPI_RSDP_SIG "RSD PTR " + +struct acpi_rsdp { + uint8_t signature[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t reserved; + uint32_t rsdt_address; +} __packed; + +/* + * Size of a buffer which can store a table signature as a string. + */ +#define ACPI_SIG_SIZE 5 + +struct acpi_sdth { + 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; +} __packed; + +struct acpi_rsdt { + struct acpi_sdth header; + uint32_t entries[0]; +} __packed; + +/* + * MADT entry type codes. + */ +#define ACPI_MADT_ENTRY_LAPIC 0 +#define ACPI_MADT_ENTRY_IOAPIC 1 + +struct acpi_madt_entry_hdr { + uint8_t type; + uint8_t length; +} __packed; + +#define ACPI_MADT_LAPIC_ENABLED 0x1 + +struct acpi_madt_entry_lapic { + struct acpi_madt_entry_hdr header; + uint8_t processor_id; + uint8_t apic_id; + uint32_t flags; +} __packed; + +struct acpi_madt_entry_ioapic { + struct acpi_madt_entry_hdr header; + uint8_t id; + uint8_t _reserved; + uint32_t addr; + uint32_t base; +} __packed; + +union acpi_madt_entry { + uint8_t type; + struct acpi_madt_entry_hdr header; + struct acpi_madt_entry_lapic lapic; + struct acpi_madt_entry_ioapic ioapic; +} __packed; + +struct acpi_madt { + struct acpi_sdth header; + uint32_t lapic_addr; + uint32_t flags; + union acpi_madt_entry entries[0]; +} __packed; + +struct acpi_madt_iter { + const union acpi_madt_entry *entry; + const union acpi_madt_entry *end; +}; + +#define acpi_madt_foreach(madt, iter) \ +for (acpi_madt_iter_init(iter, madt); \ + acpi_madt_iter_valid(iter); \ + acpi_madt_iter_next(iter)) + +struct acpi_table_addr { + const char *sig; + struct acpi_sdth *table; +}; + +static struct acpi_table_addr acpi_table_addrs[] __initdata = { + { "RSDT", NULL }, + { "APIC", NULL } +}; + +static void __init +acpi_table_sig(const struct acpi_sdth *table, char sig[ACPI_SIG_SIZE]) +{ + memcpy(sig, table->signature, sizeof(table->signature)); + sig[4] = '\0'; +} + +static int __init +acpi_table_required(const struct acpi_sdth *table) +{ + char sig[ACPI_SIG_SIZE]; + size_t i; + + acpi_table_sig(table, sig); + + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { + return 1; + } + + return 0; +} + +static void __init +acpi_register_table(struct acpi_sdth *table) +{ + char sig[ACPI_SIG_SIZE]; + size_t i; + + acpi_table_sig(table, sig); + + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { + if (acpi_table_addrs[i].table != NULL) { + printf("acpi: warning: table %s ignored:" + " already registered\n", sig); + return; + } + + acpi_table_addrs[i].table = table; + return; + } + + printf("acpi: warning: table '%s' ignored: unknown table\n", sig); +} + +static struct acpi_sdth * __init +acpi_lookup_table(const char *sig) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { + return acpi_table_addrs[i].table; + } + + return NULL; +} + +static int __init +acpi_check_tables(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + if (acpi_table_addrs[i].table == NULL) { + printf("acpi: error: table %s missing\n", + acpi_table_addrs[i].sig); + return -1; + } + + return 0; +} + +static void __init +acpi_free_tables(void) +{ + struct acpi_sdth *table; + size_t i; + + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) { + table = acpi_table_addrs[i].table; + + if (table != NULL) { + kmem_free(table, table->length); + } + } +} + +static unsigned int __init +acpi_checksum(const void *ptr, size_t size) +{ + const uint8_t *bytes; + uint8_t checksum; + size_t i; + + bytes = ptr; + checksum = 0; + + for (i = 0; i < size; i++) { + checksum += bytes[i]; + } + + return checksum; +} + +static int __init +acpi_check_rsdp(const struct acpi_rsdp *rsdp) +{ + unsigned int checksum; + + if (memcmp(rsdp->signature, ACPI_RSDP_SIG, sizeof(rsdp->signature)) != 0) { + return -1; + } + + checksum = acpi_checksum(rsdp, sizeof(*rsdp)); + + if (checksum != 0) { + return -1; + } + + return 0; +} + +static int __init +acpi_get_rsdp(phys_addr_t start, size_t size, struct acpi_rsdp *rsdp) +{ + const struct acpi_rsdp *src; + uintptr_t addr, end, map_addr; + size_t map_size; + int error; + + assert(size > 0); + assert(P2ALIGNED(size, ACPI_RSDP_ALIGN)); + + if (!P2ALIGNED(start, ACPI_RSDP_ALIGN)) { + return -1; + } + + addr = (uintptr_t)vm_kmem_map_pa(start, size, &map_addr, &map_size); + + if (addr == 0) { + panic("acpi: unable to map bios memory in kernel map"); + } + + for (end = addr + size; addr < end; addr += ACPI_RSDP_ALIGN) { + src = (const struct acpi_rsdp *)addr; + error = acpi_check_rsdp(src); + + if (!error) { + break; + } + } + + if (!(addr < end)) { + error = -1; + goto out; + } + + memcpy(rsdp, src, sizeof(*rsdp)); + error = 0; + +out: + vm_kmem_unmap_pa(map_addr, map_size); + return error; +} + +static int __init +acpi_find_rsdp(struct acpi_rsdp *rsdp) +{ + const uint16_t *ptr; + uintptr_t base, map_addr; + size_t map_size; + int error; + + ptr = vm_kmem_map_pa(BIOSMEM_EBDA_PTR, sizeof(*ptr), &map_addr, &map_size); + + if (ptr == NULL) { + panic("acpi: unable to map ebda pointer in kernel map"); + } + + base = *((const volatile uint16_t *)ptr); + vm_kmem_unmap_pa(map_addr, map_size); + + if (base != 0) { + base <<= 4; + error = acpi_get_rsdp(base, 1024, rsdp); + + if (!error) { + return 0; + } + } + + error = acpi_get_rsdp(BIOSMEM_EXT_ROM, BIOSMEM_END - BIOSMEM_EXT_ROM, + rsdp); + + if (!error) { + return 0; + } + + printf("acpi: unable to find root system description pointer\n"); + return -1; +} + +static void __init +acpi_info(void) +{ + const struct acpi_sdth *rsdt; + + rsdt = acpi_lookup_table("RSDT"); + assert(rsdt != NULL); + printf("acpi: revision: %u, oem: %.*s\n", rsdt->revision, + (int)sizeof(rsdt->oem_id), rsdt->oem_id); +} + +static struct acpi_sdth * __init +acpi_copy_table(uint32_t addr) +{ + const struct acpi_sdth *table; + struct acpi_sdth *copy; + uintptr_t map_addr; + size_t size, map_size; + unsigned int checksum; + + table = vm_kmem_map_pa(addr, sizeof(*table), &map_addr, &map_size); + + if (table == NULL) { + panic("acpi: unable to map acpi data in kernel map"); + } + + if (!acpi_table_required(table)) { + copy = NULL; + goto out; + } + + size = ((const volatile struct acpi_sdth *)table)->length; + vm_kmem_unmap_pa(map_addr, map_size); + + table = vm_kmem_map_pa(addr, size, &map_addr, &map_size); + + if (table == NULL) { + panic("acpi: unable to map acpi data in kernel map"); + } + + checksum = acpi_checksum(table, size); + + if (checksum != 0) { + char sig[ACPI_SIG_SIZE]; + + acpi_table_sig(table, sig); + printf("acpi: table %s: invalid checksum\n", sig); + copy = NULL; + goto out; + } + + copy = kmem_alloc(size); + + if (copy == NULL) { + panic("acpi: unable to allocate memory for acpi data copy"); + } + + memcpy(copy, table, size); + +out: + vm_kmem_unmap_pa(map_addr, map_size); + return copy; +} + +static int __init +acpi_copy_tables(const struct acpi_rsdp *rsdp) +{ + struct acpi_rsdt *rsdt; + struct acpi_sdth *table; + uint32_t *addr, *end; + int error; + + table = acpi_copy_table(rsdp->rsdt_address); + + if (table == NULL) { + return -1; + } + + acpi_register_table(table); + + rsdt = structof(table, struct acpi_rsdt, header); + end = (void *)rsdt + rsdt->header.length; + + for (addr = rsdt->entries; addr < end; addr++) { + table = acpi_copy_table(*addr); + + if (table == NULL) { + continue; + } + + acpi_register_table(table); + } + + error = acpi_check_tables(); + + if (error) { + goto error; + } + + return 0; + +error: + acpi_free_tables(); + return -1; +} + +static void __init +acpi_madt_iter_init(struct acpi_madt_iter *iter, + const struct acpi_madt *madt) +{ + iter->entry = madt->entries; + iter->end = (void *)madt + madt->header.length; +} + +static int __init +acpi_madt_iter_valid(const struct acpi_madt_iter *iter) +{ + return iter->entry < iter->end; +} + +static void __init +acpi_madt_iter_next(struct acpi_madt_iter *iter) +{ + iter->entry = (void *)iter->entry + iter->entry->header.length; +} + +static void __init +acpi_load_lapic(const struct acpi_madt_entry_lapic *lapic, int *is_bsp) +{ + if (!(lapic->flags & ACPI_MADT_LAPIC_ENABLED)) { + return; + } + + cpu_mp_register_lapic(lapic->apic_id, *is_bsp); + *is_bsp = 0; +} + +static void __init +acpi_load_ioapic(const struct acpi_madt_entry_ioapic *ioapic) +{ + ioapic_register(ioapic->id, ioapic->addr, ioapic->base); +} + +static void __init +acpi_load_madt(void) +{ + const struct acpi_sdth *table; + const struct acpi_madt *madt; + struct acpi_madt_iter iter; + int is_bsp; + + table = acpi_lookup_table("APIC"); + assert(table != NULL); + madt = structof(table, struct acpi_madt, header); + lapic_setup(madt->lapic_addr); + is_bsp = 1; + + /* + * TODO Handle PCAT_COMPAT flag + * TODO Handle interrupt overrides + */ + + acpi_madt_foreach(madt, &iter) { + switch (iter.entry->type) { + case ACPI_MADT_ENTRY_LAPIC: + acpi_load_lapic(&iter.entry->lapic, &is_bsp); + break; + case ACPI_MADT_ENTRY_IOAPIC: + acpi_load_ioapic(&iter.entry->ioapic); + } + } +} + +int __init +acpi_setup(void) +{ + struct acpi_rsdp rsdp; + int error; + + error = acpi_find_rsdp(&rsdp); + + if (error) { + return error; + } + + error = acpi_copy_tables(&rsdp); + + if (error) { + return error; + } + + acpi_info(); + acpi_load_madt(); + acpi_free_tables(); + return 0; +} diff --git a/arch/x86/machine/acpi.h b/arch/x86/machine/acpi.h new file mode 100644 index 0000000..b767b9a --- /dev/null +++ b/arch/x86/machine/acpi.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012-2017 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 3 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 . + * + * + * Multiprocessor information gathering module, supporting the ACPI + * specification v1.0. + */ + +#ifndef _X86_ACPI_H +#define _X86_ACPI_H + +/* + * Load ACPI information. + * + * Return 0 if successful (an error usually means hardware doesn't support + * ACPI). + */ +int acpi_setup(void); + +#endif /* _X86_ACPI_H */ diff --git a/arch/x86/machine/acpimp.c b/arch/x86/machine/acpimp.c deleted file mode 100644 index 0af1ae7..0000000 --- a/arch/x86/machine/acpimp.c +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright (c) 2012 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 3 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 -#include -#include -#include -#include - -/* - * Alignment of the RSDP. - */ -#define ACPIMP_RSDP_ALIGN 16 - -/* - * Signature of the root system description pointer. - */ -#define ACPIMP_RSDP_SIG "RSD PTR " - -struct acpimp_rsdp { - uint8_t signature[8]; - uint8_t checksum; - uint8_t oem_id[6]; - uint8_t reserved; - uint32_t rsdt_address; -} __packed; - -/* - * Size of a buffer which can store a table signature as a string. - */ -#define ACPIMP_SIG_SIZE 5 - -struct acpimp_sdth { - 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; -} __packed; - -struct acpimp_rsdt { - struct acpimp_sdth header; - uint32_t entries[0]; -} __packed; - -/* - * MADT entry type codes. - */ -#define ACPIMP_MADT_ENTRY_LAPIC 0 -#define ACPIMP_MADT_ENTRY_IOAPIC 1 - -struct acpimp_madt_entry_hdr { - uint8_t type; - uint8_t length; -} __packed; - -#define ACPIMP_MADT_LAPIC_ENABLED 0x1 - -struct acpimp_madt_entry_lapic { - struct acpimp_madt_entry_hdr header; - uint8_t processor_id; - uint8_t apic_id; - uint32_t flags; -} __packed; - -struct acpimp_madt_entry_ioapic { - struct acpimp_madt_entry_hdr header; - uint8_t id; - uint8_t _reserved; - uint32_t addr; - uint32_t base; -} __packed; - -union acpimp_madt_entry { - uint8_t type; - struct acpimp_madt_entry_hdr header; - struct acpimp_madt_entry_lapic lapic; - struct acpimp_madt_entry_ioapic ioapic; -} __packed; - -struct acpimp_madt { - struct acpimp_sdth header; - uint32_t lapic_addr; - uint32_t flags; - union acpimp_madt_entry entries[0]; -} __packed; - -struct acpimp_madt_iter { - const union acpimp_madt_entry *entry; - const union acpimp_madt_entry *end; -}; - -#define acpimp_madt_foreach(madt, iter) \ -for (acpimp_madt_iter_init(iter, madt); \ - acpimp_madt_iter_valid(iter); \ - acpimp_madt_iter_next(iter)) - -struct acpimp_table_addr { - const char *sig; - struct acpimp_sdth *table; -}; - -static struct acpimp_table_addr acpimp_table_addrs[] __initdata = { - { "RSDT", NULL }, - { "APIC", NULL } -}; - -static void __init -acpimp_table_sig(const struct acpimp_sdth *table, char sig[ACPIMP_SIG_SIZE]) -{ - memcpy(sig, table->signature, sizeof(table->signature)); - sig[4] = '\0'; -} - -static int __init -acpimp_table_required(const struct acpimp_sdth *table) -{ - char sig[ACPIMP_SIG_SIZE]; - size_t i; - - acpimp_table_sig(table, sig); - - for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) - if (strcmp(sig, acpimp_table_addrs[i].sig) == 0) { - return 1; - } - - return 0; -} - -static void __init -acpimp_register_table(struct acpimp_sdth *table) -{ - char sig[ACPIMP_SIG_SIZE]; - size_t i; - - acpimp_table_sig(table, sig); - - for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) - if (strcmp(sig, acpimp_table_addrs[i].sig) == 0) { - if (acpimp_table_addrs[i].table != NULL) { - printf("acpimp: warning: table %s ignored:" - " already registered\n", sig); - return; - } - - acpimp_table_addrs[i].table = table; - return; - } - - printf("acpimp: warning: table '%s' ignored: unknown table\n", sig); -} - -static struct acpimp_sdth * __init -acpimp_lookup_table(const char *sig) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) - if (strcmp(sig, acpimp_table_addrs[i].sig) == 0) { - return acpimp_table_addrs[i].table; - } - - return NULL; -} - -static int __init -acpimp_check_tables(void) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) - if (acpimp_table_addrs[i].table == NULL) { - printf("acpimp: error: table %s missing\n", - acpimp_table_addrs[i].sig); - return -1; - } - - return 0; -} - -static void __init -acpimp_free_tables(void) -{ - struct acpimp_sdth *table; - size_t i; - - for (i = 0; i < ARRAY_SIZE(acpimp_table_addrs); i++) { - table = acpimp_table_addrs[i].table; - - if (table != NULL) { - kmem_free(table, table->length); - } - } -} - -static unsigned int __init -acpimp_checksum(const void *ptr, size_t size) -{ - const uint8_t *bytes; - uint8_t checksum; - size_t i; - - bytes = ptr; - checksum = 0; - - for (i = 0; i < size; i++) { - checksum += bytes[i]; - } - - return checksum; -} - -static int __init -acpimp_check_rsdp(const struct acpimp_rsdp *rsdp) -{ - unsigned int checksum; - - if (memcmp(rsdp->signature, ACPIMP_RSDP_SIG, sizeof(rsdp->signature)) != 0) { - return -1; - } - - checksum = acpimp_checksum(rsdp, sizeof(*rsdp)); - - if (checksum != 0) { - return -1; - } - - return 0; -} - -static int __init -acpimp_get_rsdp(phys_addr_t start, size_t size, struct acpimp_rsdp *rsdp) -{ - const struct acpimp_rsdp *src; - uintptr_t addr, end, map_addr; - size_t map_size; - int error; - - assert(size > 0); - assert(P2ALIGNED(size, ACPIMP_RSDP_ALIGN)); - - if (!P2ALIGNED(start, ACPIMP_RSDP_ALIGN)) { - return -1; - } - - addr = (uintptr_t)vm_kmem_map_pa(start, size, &map_addr, &map_size); - - if (addr == 0) { - panic("acpimp: unable to map bios memory in kernel map"); - } - - for (end = addr + size; addr < end; addr += ACPIMP_RSDP_ALIGN) { - src = (const struct acpimp_rsdp *)addr; - error = acpimp_check_rsdp(src); - - if (!error) { - break; - } - } - - if (!(addr < end)) { - error = -1; - goto out; - } - - memcpy(rsdp, src, sizeof(*rsdp)); - error = 0; - -out: - vm_kmem_unmap_pa(map_addr, map_size); - return error; -} - -static int __init -acpimp_find_rsdp(struct acpimp_rsdp *rsdp) -{ - const uint16_t *ptr; - uintptr_t base, map_addr; - size_t map_size; - int error; - - ptr = vm_kmem_map_pa(BIOSMEM_EBDA_PTR, sizeof(*ptr), &map_addr, &map_size); - - if (ptr == NULL) { - panic("acpimp: unable to map ebda pointer in kernel map"); - } - - base = *((const volatile uint16_t *)ptr); - vm_kmem_unmap_pa(map_addr, map_size); - - if (base != 0) { - base <<= 4; - error = acpimp_get_rsdp(base, 1024, rsdp); - - if (!error) { - return 0; - } - } - - error = acpimp_get_rsdp(BIOSMEM_EXT_ROM, BIOSMEM_END - BIOSMEM_EXT_ROM, - rsdp); - - if (!error) { - return 0; - } - - printf("acpimp: unable to find root system description pointer\n"); - return -1; -} - -static void __init -acpimp_info(void) -{ - const struct acpimp_sdth *rsdt; - - rsdt = acpimp_lookup_table("RSDT"); - assert(rsdt != NULL); - printf("acpimp: revision: %u, oem: %.*s\n", rsdt->revision, - (int)sizeof(rsdt->oem_id), rsdt->oem_id); -} - -static struct acpimp_sdth * __init -acpimp_copy_table(uint32_t addr) -{ - const struct acpimp_sdth *table; - struct acpimp_sdth *copy; - uintptr_t map_addr; - size_t size, map_size; - unsigned int checksum; - - table = vm_kmem_map_pa(addr, sizeof(*table), &map_addr, &map_size); - - if (table == NULL) { - panic("acpimp: unable to map acpi data in kernel map"); - } - - if (!acpimp_table_required(table)) { - copy = NULL; - goto out; - } - - size = ((const volatile struct acpimp_sdth *)table)->length; - vm_kmem_unmap_pa(map_addr, map_size); - - table = vm_kmem_map_pa(addr, size, &map_addr, &map_size); - - if (table == NULL) { - panic("acpimp: unable to map acpi data in kernel map"); - } - - checksum = acpimp_checksum(table, size); - - if (checksum != 0) { - char sig[ACPIMP_SIG_SIZE]; - - acpimp_table_sig(table, sig); - printf("acpimp: table %s: invalid checksum\n", sig); - copy = NULL; - goto out; - } - - copy = kmem_alloc(size); - - if (copy == NULL) { - panic("acpimp: unable to allocate memory for acpi data copy"); - } - - memcpy(copy, table, size); - -out: - vm_kmem_unmap_pa(map_addr, map_size); - return copy; -} - -static int __init -acpimp_copy_tables(const struct acpimp_rsdp *rsdp) -{ - struct acpimp_rsdt *rsdt; - struct acpimp_sdth *table; - uint32_t *addr, *end; - int error; - - table = acpimp_copy_table(rsdp->rsdt_address); - - if (table == NULL) { - return -1; - } - - acpimp_register_table(table); - - rsdt = structof(table, struct acpimp_rsdt, header); - end = (void *)rsdt + rsdt->header.length; - - for (addr = rsdt->entries; addr < end; addr++) { - table = acpimp_copy_table(*addr); - - if (table == NULL) { - continue; - } - - acpimp_register_table(table); - } - - error = acpimp_check_tables(); - - if (error) { - goto error; - } - - return 0; - -error: - acpimp_free_tables(); - return -1; -} - -static void __init -acpimp_madt_iter_init(struct acpimp_madt_iter *iter, - const struct acpimp_madt *madt) -{ - iter->entry = madt->entries; - iter->end = (void *)madt + madt->header.length; -} - -static int __init -acpimp_madt_iter_valid(const struct acpimp_madt_iter *iter) -{ - return iter->entry < iter->end; -} - -static void __init -acpimp_madt_iter_next(struct acpimp_madt_iter *iter) -{ - iter->entry = (void *)iter->entry + iter->entry->header.length; -} - -static void __init -acpimp_load_lapic(const struct acpimp_madt_entry_lapic *lapic, int *is_bsp) -{ - if (!(lapic->flags & ACPIMP_MADT_LAPIC_ENABLED)) { - return; - } - - cpu_mp_register_lapic(lapic->apic_id, *is_bsp); - *is_bsp = 0; -} - -static void __init -acpimp_load_ioapic(const struct acpimp_madt_entry_ioapic *ioapic) -{ - ioapic_register(ioapic->id, ioapic->addr, ioapic->base); -} - -static void __init -acpimp_load_madt(void) -{ - const struct acpimp_sdth *table; - const struct acpimp_madt *madt; - struct acpimp_madt_iter iter; - int is_bsp; - - table = acpimp_lookup_table("APIC"); - assert(table != NULL); - madt = structof(table, struct acpimp_madt, header); - lapic_setup(madt->lapic_addr); - is_bsp = 1; - - /* - * TODO Handle PCAT_COMPAT flag - * TODO Handle interrupt overrides - */ - - acpimp_madt_foreach(madt, &iter) { - switch (iter.entry->type) { - case ACPIMP_MADT_ENTRY_LAPIC: - acpimp_load_lapic(&iter.entry->lapic, &is_bsp); - break; - case ACPIMP_MADT_ENTRY_IOAPIC: - acpimp_load_ioapic(&iter.entry->ioapic); - } - } -} - -int __init -acpimp_setup(void) -{ - struct acpimp_rsdp rsdp; - int error; - - error = acpimp_find_rsdp(&rsdp); - - if (error) { - return error; - } - - error = acpimp_copy_tables(&rsdp); - - if (error) { - return error; - } - - acpimp_info(); - acpimp_load_madt(); - acpimp_free_tables(); - return 0; -} diff --git a/arch/x86/machine/acpimp.h b/arch/x86/machine/acpimp.h deleted file mode 100644 index e66a87a..0000000 --- a/arch/x86/machine/acpimp.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2012 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 3 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 . - * - * - * Multiprocessor information gathering module, supporting the ACPI - * specification v1.0. - */ - -#ifndef _X86_ACPIMP_H -#define _X86_ACPIMP_H - -/* - * Load multiprocessor information. - * - * Return 0 if successful (an error usually means hardware doesn't support - * ACPI). - */ -int acpimp_setup(void); - -#endif /* _X86_ACPIMP_H */ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 7c60849..7684f56 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -643,7 +643,7 @@ cpu_mp_probe(void) { int error; - error = acpimp_setup(); + error = acpi_setup(); if (error) { /* -- cgit v1.2.3 From ab6aa05afffdbc9c5a0969f37d0b989549de1a9a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 15:46:59 +0200 Subject: x86/pic: new pic_setup_disabled function This function is meant to initialize the legacy PIC in a disabled state, for use in APIC systems where legacy hardware is still present. --- arch/x86/machine/pic.c | 19 ++++++++++++++++--- arch/x86/machine/pic.h | 13 ++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index 52f9461..ab30ac2 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -198,8 +199,8 @@ pic_spurious_intr(void *arg) return 0; } -void __init -pic_setup(void) +static void __init +pic_setup_common(bool register_ctl) { int error; @@ -227,7 +228,7 @@ pic_setup(void) io_write_byte(PIC_MASTER_IMR, pic_master_mask); io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); - if (lapic_unused()) { + if (register_ctl) { pic_register(); } @@ -241,3 +242,15 @@ pic_setup(void) &pic_slave_spurious_intr); error_check(error, __func__); } + +void __init +pic_setup(void) +{ + pic_setup_common(true); +} + +void __init +pic_setup_disabled(void) +{ + pic_setup_common(false); +} diff --git a/arch/x86/machine/pic.h b/arch/x86/machine/pic.h index 9da75b7..e49d336 100644 --- a/arch/x86/machine/pic.h +++ b/arch/x86/machine/pic.h @@ -21,8 +21,19 @@ #include /* - * Set up the pic module. + * Initialize the pic module. */ void pic_setup(void); +/* + * Initialize the pic module in an APIC system. + * + * This function is called by the acpi module if ACPI reports the presence + * of legacy interrupt controllers. + * + * Since it doesn't register the legacy PIC as an interrupt controller, the + * acpi module must have registered I/O APICs before calling this function. + */ +void pic_setup_disabled(void); + #endif /* _X86_PIC_H */ -- cgit v1.2.3 From 6bdd90785b1519f7b71d79390c0b76d2299445e5 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 15:48:16 +0200 Subject: x86/acpi: handle the PCAT_COMPAT flag --- arch/x86/machine/acpi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 5927e95..1f7e568 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,8 @@ union acpi_madt_entry { struct acpi_madt_entry_ioapic ioapic; } __packed; +#define ACPI_MADT_PC_COMPAT 0x1 + struct acpi_madt { struct acpi_sdth header; uint32_t lapic_addr; @@ -494,11 +497,11 @@ acpi_load_madt(void) table = acpi_lookup_table("APIC"); assert(table != NULL); madt = structof(table, struct acpi_madt, header); + lapic_setup(madt->lapic_addr); is_bsp = 1; /* - * TODO Handle PCAT_COMPAT flag * TODO Handle interrupt overrides */ @@ -511,6 +514,10 @@ acpi_load_madt(void) acpi_load_ioapic(&iter.entry->ioapic); } } + + if (madt->flags & ACPI_MADT_PC_COMPAT) { + pic_setup_disabled(); + } } int __init -- cgit v1.2.3 From 04002b5583c6da62b60b9c2470b0853178358658 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 15:49:00 +0200 Subject: x86: don't unconditionnally initialize the legacy PIC --- arch/x86/machine/boot.c | 2 -- arch/x86/machine/cpu.c | 3 ++- arch/x86/machine/lapic.c | 19 ------------------- arch/x86/machine/lapic.h | 11 ----------- 4 files changed, 2 insertions(+), 33 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 84960eb..bca02f8 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -71,7 +71,6 @@ #include #include #include -#include #include #include #include @@ -487,7 +486,6 @@ boot_main(void) biosmem_free_usable(); intr_setup(); cpu_mp_probe(); - pic_setup(); atcons_setup(); uart_setup(); kernel_main(); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 7684f56..451bef1 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -652,7 +653,7 @@ cpu_mp_probe(void) * ACPI only. If ACPI is unavailable, consider the APIC system to * be missing and fall back to using the legaxy XT-PIC. */ - lapic_setup_unused(); + pic_setup(); } printf("cpu: %u processor(s) configured\n", cpu_count()); diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index 148a2a9..dad3bd7 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -184,9 +184,6 @@ static volatile struct lapic_map *lapic_map __read_mostly; */ static uint32_t lapic_bus_freq __read_mostly; -static bool lapic_initialized __initdata; -static bool lapic_is_unused __initdata; - static uint32_t lapic_read(const volatile struct lapic_register *r) { @@ -245,20 +242,6 @@ lapic_setup_registers(void) lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); } -bool __init -lapic_unused(void) -{ - assert(lapic_initialized); - return lapic_is_unused; -} - -void __init -lapic_setup_unused(void) -{ - lapic_initialized = true; - lapic_is_unused = true; -} - void __init lapic_setup(uint32_t map_addr) { @@ -278,8 +261,6 @@ lapic_setup(uint32_t map_addr) lapic_compute_freq(); lapic_setup_registers(); - - lapic_initialized = true; } void __init diff --git a/arch/x86/machine/lapic.h b/arch/x86/machine/lapic.h index ff9c893..ae7abfe 100644 --- a/arch/x86/machine/lapic.h +++ b/arch/x86/machine/lapic.h @@ -28,17 +28,6 @@ */ void lapic_eoi(void); -/* - * Report whether the local APIC is actually used or not. - */ -bool lapic_unused(void); - -/* - * Initialize the lapic module for the sole purpose of reporting that - * it's actually not used. - */ -void lapic_setup_unused(void); - /* * Set up the lapic module. */ -- cgit v1.2.3 From bcac7e2c417bdbc0c2eace0a693d29442a0db12c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 3 Jun 2017 17:42:53 +0200 Subject: x86/{acpi,ioapic}: handle interrupt source overrides --- arch/x86/machine/acpi.c | 67 +++++++++++++++++++++++-- arch/x86/machine/ioapic.c | 121 ++++++++++++++++++++++++++++++++++++++++++++-- arch/x86/machine/ioapic.h | 12 +++++ arch/x86/machine/pic.c | 2 - arch/x86/machine/pic.h | 10 +++- arch/x86/machine/trap.h | 5 +- 6 files changed, 203 insertions(+), 14 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 1f7e568..6d5e06e 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -80,6 +81,7 @@ struct acpi_rsdt { */ #define ACPI_MADT_ENTRY_LAPIC 0 #define ACPI_MADT_ENTRY_IOAPIC 1 +#define ACPI_MADT_ENTRY_ISO 2 struct acpi_madt_entry_hdr { uint8_t type; @@ -103,11 +105,29 @@ struct acpi_madt_entry_ioapic { uint32_t base; } __packed; +#define ACPI_MADT_ISO_POL_DEFAULT 0x00 +#define ACPI_MADT_ISO_POL_HIGH 0x01 +#define ACPI_MADT_ISO_POL_LOW 0x03 +#define ACPI_MADT_ISO_POL_MASK 0x03 +#define ACPI_MADT_ISO_TRIG_DEFAULT 0x00 +#define ACPI_MADT_ISO_TRIG_EDGE 0x04 +#define ACPI_MADT_ISO_TRIG_LEVEL 0x0c +#define ACPI_MADT_ISO_TRIG_MASK 0x0c + +struct acpi_madt_entry_iso { + struct acpi_madt_entry_hdr header; + uint8_t bus; + uint8_t source; + uint32_t gsi; + int16_t flags; +} __packed; + union acpi_madt_entry { uint8_t type; struct acpi_madt_entry_hdr header; struct acpi_madt_entry_lapic lapic; struct acpi_madt_entry_ioapic ioapic; + struct acpi_madt_entry_iso iso; } __packed; #define ACPI_MADT_PC_COMPAT 0x1 @@ -486,6 +506,45 @@ acpi_load_ioapic(const struct acpi_madt_entry_ioapic *ioapic) ioapic_register(ioapic->id, ioapic->addr, ioapic->base); } +static void __init +acpi_load_iso(const struct acpi_madt_entry_iso *iso) +{ + bool active_high, edge_triggered; + + if (iso->bus != 0) { + printf("acpi: error: invalid interrupt source override bus\n"); + return; + } + + switch (iso->flags & ACPI_MADT_ISO_POL_MASK) { + case ACPI_MADT_ISO_POL_DEFAULT: + case ACPI_MADT_ISO_POL_HIGH: + active_high = true; + break; + case ACPI_MADT_ISO_POL_LOW: + active_high = false; + break; + default: + printf("acpi: error: invalid polarity\n"); + return; + } + + switch (iso->flags & ACPI_MADT_ISO_TRIG_MASK) { + case ACPI_MADT_ISO_TRIG_DEFAULT: + case ACPI_MADT_ISO_TRIG_EDGE: + edge_triggered = true; + break; + case ACPI_MADT_ISO_TRIG_LEVEL: + edge_triggered = false; + break; + default: + printf("acpi: error: invalid trigger mode\n"); + return; + } + + ioapic_override(iso->source, iso->gsi, active_high, edge_triggered); +} + static void __init acpi_load_madt(void) { @@ -501,10 +560,6 @@ acpi_load_madt(void) lapic_setup(madt->lapic_addr); is_bsp = 1; - /* - * TODO Handle interrupt overrides - */ - acpi_madt_foreach(madt, &iter) { switch (iter.entry->type) { case ACPI_MADT_ENTRY_LAPIC: @@ -512,6 +567,10 @@ acpi_load_madt(void) break; case ACPI_MADT_ENTRY_IOAPIC: acpi_load_ioapic(&iter.entry->ioapic); + break; + case ACPI_MADT_ENTRY_ISO: + acpi_load_iso(&iter.entry->iso); + break; } } diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index d48cc30..e49cb30 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,10 @@ #define IOAPIC_VERSION_MAXREDIR_MASK 0x00ff0000 #define IOAPIC_VERSION_MAXREDIR_SHIFT 16 +#define IOAPIC_ENTLOW_FIXED_DEST 0x00000 +#define IOAPIC_ENTLOW_PHYS_DELIVERY 0x00000 +#define IOAPIC_ENTLOW_ACTIVE_LOW 0x02000 +#define IOAPIC_ENTLOW_LEVEL 0x08000 #define IOAPIC_ENTLOW_INTRMASK 0x10000 #define IOAPIC_MAX_ENTRIES 24 @@ -55,6 +60,16 @@ struct ioapic_map { uint32_t win; }; +/* + * Interrupt source override descriptor. + */ +struct ioapic_iso { + uint8_t source; + uint32_t gsi; + bool active_high; + bool edge_triggered; +}; + struct ioapic { struct spinlock lock; unsigned int id; @@ -67,6 +82,51 @@ struct ioapic { static unsigned int ioapic_nr_devs; +static struct ioapic_iso ioapic_isos[PIC_MAX_INTR + 1]; +static unsigned int ioapic_nr_isos; + +static void +ioapic_iso_init(struct ioapic_iso *iso, uint8_t source, uint32_t gsi, + bool active_high, bool edge_triggered) +{ + iso->source = source; + iso->gsi = gsi; + iso->active_high = active_high; + iso->edge_triggered = edge_triggered; +} + +static struct ioapic_iso * __init +ioapic_alloc_iso(void) +{ + struct ioapic_iso *iso; + + if (ioapic_nr_isos >= ARRAY_SIZE(ioapic_isos)) { + printf("ioapic: error: too many interrupt overrides\n"); + return NULL; + } + + iso = &ioapic_isos[ioapic_nr_isos]; + ioapic_nr_isos++; + return iso; +} + +static struct ioapic_iso * +ioapic_lookup_iso(unsigned int intr) +{ + struct ioapic_iso *iso; + unsigned int i; + + for (i = 0; i < ioapic_nr_isos; i++) { + iso = &ioapic_isos[i]; + + if (intr == iso->source) { + return iso; + } + } + + return NULL; +} + static uint32_t ioapic_read(struct ioapic *ioapic, uint8_t reg) { @@ -160,19 +220,53 @@ ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) return intr - ioapic->first_intr; } +static void +ioapic_compute_entry(uint32_t *highp, uint32_t *lowp, + unsigned int apic_id, unsigned int intr, + bool active_high, bool edge_triggered) +{ + assert(apic_id < 16); + assert(intr < (TRAP_NR_VECTORS - TRAP_INTR_FIRST)); + + *highp = apic_id << 24; + *lowp = (!edge_triggered ? IOAPIC_ENTLOW_LEVEL : 0) + | (!active_high ? IOAPIC_ENTLOW_ACTIVE_LOW : 0) + | IOAPIC_ENTLOW_PHYS_DELIVERY + | IOAPIC_ENTLOW_FIXED_DEST + | (TRAP_INTR_FIRST + intr); +} + static void ioapic_enable(void *priv, unsigned int intr, unsigned int cpu) { + bool active_high, edge_triggered; + const struct ioapic_iso *iso; + uint32_t high, low, gsi; struct ioapic *ioapic; unsigned long flags; unsigned int id; + iso = ioapic_lookup_iso(intr); + + /* XXX These are defaults that should work with architectural devices */ + if (iso == NULL) { + active_high = true; + edge_triggered = true; + gsi = intr; + } else { + active_high = iso->active_high; + edge_triggered = iso->edge_triggered; + gsi = iso->gsi; + } + ioapic = priv; - id = ioapic_compute_id(ioapic, intr); + id = ioapic_compute_id(ioapic, gsi); + ioapic_compute_entry(&high, &low, cpu_apic_id(cpu), intr, + active_high, edge_triggered); spinlock_lock_intr_save(&ioapic->lock, &flags); - ioapic_write_entry_high(ioapic, id, cpu_apic_id(cpu) << 24); - ioapic_write_entry_low(ioapic, id, TRAP_INTR_FIRST + intr); + ioapic_write_entry_high(ioapic, id, high); + ioapic_write_entry_low(ioapic, id, low); spinlock_unlock_intr_restore(&ioapic->lock, flags); } @@ -206,6 +300,13 @@ static const struct intr_ops ioapic_ops = { .eoi = ioapic_eoi, }; +void __init +ioapic_setup(void) +{ + ioapic_nr_devs = 0; + ioapic_nr_isos = 0; +} + void __init ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) { @@ -216,3 +317,17 @@ ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) ioapic->first_intr, ioapic->last_intr); } +void __init +ioapic_override(uint8_t source, uint32_t gsi, + bool active_high, bool edge_triggered) +{ + struct ioapic_iso *iso; + + iso = ioapic_alloc_iso(); + + if (iso == NULL) { + return; + } + + ioapic_iso_init(iso, source, gsi, active_high, edge_triggered); +} diff --git a/arch/x86/machine/ioapic.h b/arch/x86/machine/ioapic.h index d508904..b9a702d 100644 --- a/arch/x86/machine/ioapic.h +++ b/arch/x86/machine/ioapic.h @@ -18,12 +18,24 @@ #ifndef _KERN_IOAPIC_H #define _KERN_IOAPIC_H +#include #include +/* + * Initialize the ioapic module. + */ +void ioapic_setup(void); + /* * Register an I/O APIC controller. */ void ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int gsi_base); +/* + * Report an interrupt source override. + */ +void ioapic_override(uint8_t source, uint32_t gsi, + bool active_high, bool edge_triggered); + #endif /* _KERN_IOAPIC_H */ diff --git a/arch/x86/machine/pic.c b/arch/x86/machine/pic.c index ab30ac2..8ef5753 100644 --- a/arch/x86/machine/pic.c +++ b/arch/x86/machine/pic.c @@ -51,8 +51,6 @@ */ #define PIC_SLAVE_INTR 2 #define PIC_SPURIOUS_INTR 7 -#define PIC_NR_INTRS 8 -#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1) static unsigned int pic_nr_slave_intrs; diff --git a/arch/x86/machine/pic.h b/arch/x86/machine/pic.h index e49d336..89833d0 100644 --- a/arch/x86/machine/pic.h +++ b/arch/x86/machine/pic.h @@ -18,7 +18,15 @@ #ifndef _X86_PIC_H #define _X86_PIC_H -#include +/* + * Interrupts per PIC. + */ +#define PIC_NR_INTRS 8 + +/* + * Maximum global interrupt number. + */ +#define PIC_MAX_INTR ((PIC_NR_INTRS * 2) - 1) /* * Initialize the pic module. diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index 7bda9cf..e4d5912 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -61,10 +61,7 @@ #define TRAP_LAPIC_ERROR 254 #define TRAP_LAPIC_SPURIOUS 255 -/* - * Vector identifying an unhandled trap. - */ -#define TRAP_DEFAULT 256 +#define TRAP_NR_VECTORS 256 #ifndef __ASSEMBLER__ -- cgit v1.2.3 From f074dac967f7f64ddf1c7f7dfb226b5acd9e2610 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 00:57:35 +0200 Subject: x86/boot: improve clarity with segment selector macros --- arch/x86/machine/boot_asm.S | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/arch/x86/machine/boot_asm.S b/arch/x86/machine/boot_asm.S index 3015b1f..6ae6044 100644 --- a/arch/x86/machine/boot_asm.S +++ b/arch/x86/machine/boot_asm.S @@ -22,6 +22,14 @@ #include #include +/* + * Boot GDT segment selectors. + */ +#define BOOT_GDT_SEL_NULL 0 +#define BOOT_GDT_SEL_CODE 8 +#define BOOT_GDT_SEL_DATA 16 +#define BOOT_GDT_SEL_CODE64 24 + /* * Convert a physical address in the .boot section to its real address in * the MP trampoline code. @@ -48,14 +56,14 @@ ASM_ENTRY(_start) lgdt boot_gdtr /* Keep %eax and %ebx */ - movl $0x10, %ecx + movl $BOOT_GDT_SEL_DATA, %ecx movl %ecx, %ds movl %ecx, %es movl %ecx, %ss xorl %ecx, %ecx movl %ecx, %fs movl %ecx, %gs - ljmp $8, $1f + ljmp $BOOT_GDT_SEL_CODE, $1f 1: movl $(boot_stack + STACK_SIZE), %esp @@ -68,7 +76,7 @@ ASM_ENTRY(_start) * At this point, the processor runs in long mode, but still uses the * compatibility mode code segment. Switch to 64-bit mode with a far return. */ - pushl $0x18 + pushl $BOOT_GDT_SEL_CODE64 pushl $1f lret @@ -91,7 +99,7 @@ ASM_ENTRY(_start) movl %cr0, %eax orl $CPU_CR0_PG, %eax movl %eax, %cr0 - ljmp $8, $1f + ljmp $BOOT_GDT_SEL_CODE, $1f 1: movl $(boot_stack + STACK_SIZE), %esp @@ -205,7 +213,7 @@ ASM_ENTRY(boot_setup_long_mode) movl %cr0, %eax orl $CPU_CR0_PG, %eax movl %eax, %cr0 - ljmp $8, $1f + ljmp $BOOT_GDT_SEL_CODE, $1f 1: movl %edi, %eax @@ -223,21 +231,21 @@ ASM_ENTRY(boot_ap_start32) * is enabled. */ lgdt boot_gdtr - movl $0x10, %eax + movl $BOOT_GDT_SEL_DATA, %eax movl %eax, %ds movl %eax, %es movl %eax, %ss xorl %eax, %eax movl %eax, %fs movl %eax, %gs - ljmp $8, $1f + ljmp $BOOT_GDT_SEL_CODE, $1f 1: movl $(boot_ap_stack + STACK_SIZE), %esp #ifdef __LP64__ call boot_setup_long_mode - pushl $0x18 + pushl $BOOT_GDT_SEL_CODE64 pushl $1f lret @@ -254,7 +262,7 @@ ASM_ENTRY(boot_ap_start32) movl %cr0, %eax orl $CPU_CR0_PG, %eax movl %eax, %cr0 - ljmp $8, $1f + ljmp $BOOT_GDT_SEL_CODE, $1f 1: #endif /* __LP64__ */ @@ -296,19 +304,19 @@ ASM_ENTRY(boot_mp_trampoline) movl %cr0, %eax orl $CPU_CR0_PE, %eax movl %eax, %cr0 - ljmp $8, $BOOT_MP_ADDR_PTOT(1f) + ljmp $BOOT_GDT_SEL_CODE, $BOOT_MP_ADDR_PTOT(1f) .align 4 1: .code32 - movl $0x10, %eax + movl $BOOT_GDT_SEL_DATA, %eax movl %eax, %ds movl %eax, %es movl %eax, %ss xorl %eax, %eax movl %eax, %fs movl %eax, %gs - ljmp $8, $boot_ap_start32 + ljmp $BOOT_GDT_SEL_CODE, $boot_ap_start32 ASM_END(boot_mp_trampoline) ASM_DATA(boot_ap_gdtr) @@ -317,9 +325,9 @@ ASM_DATA(boot_ap_gdtr) ASM_END(boot_ap_gdtr) ASM_DATA(boot_gdt) - .quad 0x0000000000000000 /* Null selector */ - .quad 0x00cf9a000000ffff /* Code segment selector */ - .quad 0x00cf92000000ffff /* Data segment selector */ + .quad 0x0000000000000000 /* Null descriptor */ + .quad 0x00cf9a000000ffff /* Code segment descriptor */ + .quad 0x00cf92000000ffff /* Data segment descriptor */ #ifdef __LP64__ .quad 0x00209a0000000000 /* 64-bit code segment selector */ -- cgit v1.2.3 From 8438bcc3168a4112487efaa60ff6d791b386706f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 01:06:38 +0200 Subject: x86: support stack smashing protection --- Makefile.am | 1 - arch/x86/Makefrag.am | 2 ++ arch/x86/machine/boot.c | 21 ++++++++++++++ arch/x86/machine/boot_asm.S | 67 ++++++++++++++++++++++++++++++++++++++++++++- arch/x86/machine/cpu.c | 64 ++++++++++++++++++++----------------------- arch/x86/machine/cpu.h | 41 ++++++++++++++++++++++++++- arch/x86/machine/cpu_asm.S | 3 -- arch/x86/machine/ssp.c | 30 ++++++++++++++++++++ arch/x86/machine/ssp.h | 32 ++++++++++++++++++++++ 9 files changed, 221 insertions(+), 40 deletions(-) create mode 100644 arch/x86/machine/ssp.c create mode 100644 arch/x86/machine/ssp.h diff --git a/Makefile.am b/Makefile.am index 7730313..50baae9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,7 +32,6 @@ AM_CFLAGS += \ -fsigned-char \ -fno-common -# TODO Add stack protector support AM_CFLAGS += -fno-stack-protector AM_CFLAGS += -nostdlib diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 4ec1f7a..d4f9fe7 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -57,6 +57,8 @@ x15_SOURCES += \ arch/x86/machine/pit.h \ arch/x86/machine/pmap.c \ arch/x86/machine/pmap.h \ + arch/x86/machine/ssp.c \ + arch/x86/machine/ssp.h \ arch/x86/machine/strace.c \ arch/x86/machine/strace.h \ arch/x86/machine/string.c \ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index bca02f8..5600164 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -91,6 +91,27 @@ char boot_panic_long_mode_msg[] __bootdata = "boot: processor doesn't support long mode"; #endif /* __LP64__ */ +struct cpu_tls_seg boot_tls_seg __bootdata = { + .ssp_guard_word = SSP_GUARD_WORD, +}; + +/* + * This TLS segment descriptor is copied early at boot time into the + * temporary boot GDT. However, it is incomplete. Assembly code + * completes it before writing it into the boot GDT before calling + * any C function, since those may require the TLS segment ready if + * stack protection is enabled. + */ +struct cpu_seg_desc boot_tls_seg_desc __bootdata = { + .low = (sizeof(boot_tls_seg) & 0xffff), + + .high = CPU_DESC_DB + | ((sizeof(boot_tls_seg) >> 16) & 0xf) + | CPU_DESC_PRESENT + | CPU_DESC_S + | CPU_DESC_TYPE_DATA, +}; + /* * Copies of the multiboot data passed by the boot loader. */ diff --git a/arch/x86/machine/boot_asm.S b/arch/x86/machine/boot_asm.S index 6ae6044..dc9c9a4 100644 --- a/arch/x86/machine/boot_asm.S +++ b/arch/x86/machine/boot_asm.S @@ -29,6 +29,7 @@ #define BOOT_GDT_SEL_CODE 8 #define BOOT_GDT_SEL_DATA 16 #define BOOT_GDT_SEL_CODE64 24 +#define BOOT_GDT_SEL_TLS 24 /* * Convert a physical address in the .boot section to its real address in @@ -70,6 +71,7 @@ ASM_ENTRY(_start) #ifdef __LP64__ call boot_check_long_mode + call boot_setup_tls call boot_setup_long_mode /* @@ -85,6 +87,10 @@ ASM_ENTRY(_start) movl %ebx, %edi movl %eax, %esi #else /* __LP64__ */ + call boot_build_tls_seg_desc + movl $BOOT_GDT_SEL_TLS, %ecx + movl %ecx, %gs + pushl %eax pushl %ebx #endif /* __LP64__ */ @@ -219,6 +225,62 @@ ASM_ENTRY(boot_setup_long_mode) movl %edi, %eax ret ASM_END(boot_setup_long_mode) + +ASM_ENTRY(boot_setup_tls) + pushl %eax + movl $boot_tls_seg, %eax + movl $0, %edx + movl $CPU_MSR_GSBASE, %ecx + wrmsr + popl %eax + ret +ASM_END(boot_setup_tls) + +#else /* __LP64__ */ + +/* + * Complete the temporary boot TLS segment descriptor and copy it into + * the boot GDT. + * + * The %eax and %ebx registers must be preserved. + */ +ASM_ENTRY(boot_build_tls_seg_desc) + pushl %eax + + /* Load boot_tls_seg_desc address */ + movl $boot_tls_seg_desc, %ecx + + /* Add the base 15:0 bits to boot_tls_seg_desc.low */ + movl $boot_tls_seg, %eax + shll $16, %eax + orl %eax, (%ecx) + + /* Add the base 23:16 bits to boot_tls_seg_desc.high */ + movl $boot_tls_seg, %eax + shrl $16, %eax + andl $0xff, %eax + orl %eax, 4(%ecx) + + /* Add the base 31:24 bits to boot_tls_seg_desc.high */ + movl $boot_tls_seg, %eax + andl $0xff000000, %eax + orl %eax, 4(%ecx) + + /* Load boot_gdt[BOOT_GDT_SEL_TLS] address */ + movl $boot_gdt + BOOT_GDT_SEL_TLS, %edx + + /* Set boot_gdt[BOOT_GDT_SEL_TLS].low */ + movl (%ecx), %eax + movl %eax, (%edx) + + /* Set boot_gdt[BOOT_GDT_SEL_TLS].high */ + movl 4(%ecx), %eax + movl %eax, 4(%edx) + + popl %eax + ret +ASM_END(boot_build_tls_seg_desc) + #endif /* __LP64__ */ /* @@ -237,6 +299,7 @@ ASM_ENTRY(boot_ap_start32) movl %eax, %ss xorl %eax, %eax movl %eax, %fs + movl $BOOT_GDT_SEL_TLS, %eax movl %eax, %gs ljmp $BOOT_GDT_SEL_CODE, $1f @@ -244,6 +307,7 @@ ASM_ENTRY(boot_ap_start32) movl $(boot_ap_stack + STACK_SIZE), %esp #ifdef __LP64__ + call boot_setup_tls call boot_setup_long_mode pushl $BOOT_GDT_SEL_CODE64 pushl $1f @@ -328,9 +392,10 @@ ASM_DATA(boot_gdt) .quad 0x0000000000000000 /* Null descriptor */ .quad 0x00cf9a000000ffff /* Code segment descriptor */ .quad 0x00cf92000000ffff /* Data segment descriptor */ - #ifdef __LP64__ .quad 0x00209a0000000000 /* 64-bit code segment selector */ +#else /* __LP64__ */ + .quad 0x0 /* TLS segment descriptor, filled at boot time */ #endif /* __LP64__ */ ASM_END(boot_gdt) boot_gdt_end: diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 451bef1..e9f75a8 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -75,29 +76,6 @@ #define CPU_MP_CMOS_DATA_RESET_WARM 0x0a #define CPU_MP_CMOS_RESET_VECTOR 0x467 -/* - * Gate/segment descriptor bits and masks. - */ -#define CPU_DESC_TYPE_DATA 0x00000200 -#define CPU_DESC_TYPE_CODE 0x00000a00 -#define CPU_DESC_TYPE_TSS 0x00000900 -#define CPU_DESC_TYPE_GATE_INTR 0x00000e00 -#define CPU_DESC_TYPE_GATE_TASK 0x00000500 -#define CPU_DESC_S 0x00001000 -#define CPU_DESC_PRESENT 0x00008000 -#define CPU_DESC_LONG 0x00200000 -#define CPU_DESC_DB 0x00400000 -#define CPU_DESC_GRAN_4KB 0x00800000 - -#define CPU_DESC_GATE_OFFSET_LOW_MASK 0x0000ffff -#define CPU_DESC_GATE_OFFSET_HIGH_MASK 0xffff0000 -#define CPU_DESC_SEG_IST_MASK 0x00000007 -#define CPU_DESC_SEG_BASE_LOW_MASK 0x0000ffff -#define CPU_DESC_SEG_BASE_MID_MASK 0x00ff0000 -#define CPU_DESC_SEG_BASE_HIGH_MASK 0xff000000 -#define CPU_DESC_SEG_LIMIT_LOW_MASK 0x0000ffff -#define CPU_DESC_SEG_LIMIT_HIGH_MASK 0x000f0000 - /* * Gate descriptor. */ @@ -110,14 +88,6 @@ struct cpu_gate_desc { #endif /* __LP64__ */ }; -/* - * Code or data segment descriptor. - */ -struct cpu_seg_desc { - uint32_t low; - uint32_t high; -}; - /* * LDT or TSS system segment descriptor. */ @@ -152,6 +122,16 @@ unsigned int cpu_nr_active __read_mostly; */ static uint64_t cpu_freq __read_mostly; +/* + * TLS segment, as expected by the compiler. + * + * TLS isn't actually used inside the kernel. The current purpose of this + * segment is to implement stack protection. + */ +static const struct cpu_tls_seg cpu_tls_seg = { + .ssp_guard_word = SSP_GUARD_WORD, +}; + /* * Interrupt descriptor table. */ @@ -278,9 +258,10 @@ cpu_seg_set_tss(char *table, unsigned int selector, struct cpu_tss *tss) /* * Set the given GDT for the current processor. * - * On i386, the ds, es and ss segment registers are reloaded. In any case, - * the gs segment register is set to the null selector. The fs segment - * register, which points to the percpu area, must be set separately. + * On i386, the ds, es and ss segment registers are reloaded. + * + * The fs and gs segment registers, which point to the percpu and the TLS + * areas respectively, must be set separately. */ void cpu_load_gdt(struct cpu_pseudo_desc *gdtr); @@ -299,6 +280,19 @@ cpu_set_percpu_area(const struct cpu *cpu, void *area) percpu_var(cpu_local_area, cpu->id) = area; } +static inline void __init +cpu_set_tls_area(void) +{ +#ifdef __LP64__ + unsigned long va; + + va = (unsigned long)&cpu_tls_seg; + cpu_set_msr(CPU_MSR_GSBASE, (uint32_t)(va >> 32), (uint32_t)va); +#else /* __LP64__ */ + asm volatile("mov %0, %%gs" : : "r" (CPU_GDT_SEL_TLS)); +#endif /* __LP64__ */ +} + static void __init cpu_init_gdtr(struct cpu_pseudo_desc *gdtr, const struct cpu *cpu) { @@ -322,11 +316,13 @@ cpu_init_gdt(struct cpu *cpu) #ifndef __LP64__ cpu_seg_set_tss(cpu->gdt, CPU_GDT_SEL_DF_TSS, &cpu->double_fault_tss); cpu_seg_set_data(cpu->gdt, CPU_GDT_SEL_PERCPU, (unsigned long)pcpu_area); + cpu_seg_set_data(cpu->gdt, CPU_GDT_SEL_TLS, (unsigned long)&cpu_tls_seg); #endif /* __LP64__ */ cpu_init_gdtr(&gdtr, cpu); cpu_load_gdt(&gdtr); cpu_set_percpu_area(cpu, pcpu_area); + cpu_set_tls_area(); } static void __init diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 4e4ccbb..6e0f19a 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -54,6 +54,7 @@ */ #define CPU_MSR_EFER 0xc0000080 #define CPU_MSR_FSBASE 0xc0000100 +#define CPU_MSR_GSBASE 0xc0000101 /* * EFER MSR flags. @@ -89,7 +90,8 @@ #else /* __LP64__ */ #define CPU_GDT_SEL_DF_TSS 32 #define CPU_GDT_SEL_PERCPU 40 -#define CPU_GDT_SIZE 48 +#define CPU_GDT_SEL_TLS 48 +#define CPU_GDT_SIZE 56 #endif /* __LP64__ */ #define CPU_IDT_SIZE 256 @@ -102,6 +104,38 @@ #include #include #include +#include + +/* + * Gate/segment descriptor bits and masks. + */ +#define CPU_DESC_TYPE_DATA 0x00000200 +#define CPU_DESC_TYPE_CODE 0x00000a00 +#define CPU_DESC_TYPE_TSS 0x00000900 +#define CPU_DESC_TYPE_GATE_INTR 0x00000e00 +#define CPU_DESC_TYPE_GATE_TASK 0x00000500 +#define CPU_DESC_S 0x00001000 +#define CPU_DESC_PRESENT 0x00008000 +#define CPU_DESC_LONG 0x00200000 +#define CPU_DESC_DB 0x00400000 +#define CPU_DESC_GRAN_4KB 0x00800000 + +#define CPU_DESC_GATE_OFFSET_LOW_MASK 0x0000ffff +#define CPU_DESC_GATE_OFFSET_HIGH_MASK 0xffff0000 +#define CPU_DESC_SEG_IST_MASK 0x00000007 +#define CPU_DESC_SEG_BASE_LOW_MASK 0x0000ffff +#define CPU_DESC_SEG_BASE_MID_MASK 0x00ff0000 +#define CPU_DESC_SEG_BASE_HIGH_MASK 0xff000000 +#define CPU_DESC_SEG_LIMIT_LOW_MASK 0x0000ffff +#define CPU_DESC_SEG_LIMIT_HIGH_MASK 0x000f0000 + +/* + * Code or data segment descriptor. + */ +struct cpu_seg_desc { + uint32_t low; + uint32_t high; +}; /* * Forward declaration. @@ -189,6 +223,11 @@ struct cpu { void *double_fault_stack; }; +struct cpu_tls_seg { + uintptr_t unused[SSP_WORD_TLS_OFFSET]; + uintptr_t ssp_guard_word; +}; + /* * Macro to create functions that read/write control registers. */ diff --git a/arch/x86/machine/cpu_asm.S b/arch/x86/machine/cpu_asm.S index 9f8c077..9d47991 100644 --- a/arch/x86/machine/cpu_asm.S +++ b/arch/x86/machine/cpu_asm.S @@ -34,9 +34,6 @@ ASM_ENTRY(cpu_load_gdt) movl %eax, %es movl %eax, %ss - movl $CPU_GDT_SEL_NULL, %eax - movl %eax, %gs - /* Alter the stack to reload the code segment using a far return */ #ifdef __LP64__ popq %rax diff --git a/arch/x86/machine/ssp.c b/arch/x86/machine/ssp.c new file mode 100644 index 0000000..77d5a2b --- /dev/null +++ b/arch/x86/machine/ssp.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 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 3 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 + +void ssp_panic(void); + +void +ssp_panic(void) +{ + panic("ssp: stack corruption detected"); +} + +void __stack_chk_fail(void) __attribute__((alias("ssp_panic"))); diff --git a/arch/x86/machine/ssp.h b/arch/x86/machine/ssp.h new file mode 100644 index 0000000..f278361 --- /dev/null +++ b/arch/x86/machine/ssp.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 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 3 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_SSP_H +#define _X86_SSP_H + +#ifdef __LP64__ +#define SSP_GUARD_WORD 0xdeadd00ddeadd00d +#else +#define SSP_GUARD_WORD 0xdeadd00d +#endif + +/* + * Offset, in words, of the SSP guard word. + */ +#define SSP_WORD_TLS_OFFSET 5 + +#endif /* _X86_SSP_H */ -- cgit v1.2.3 From 2825ff20536909c88b4692108be605b5e5daa9e6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 12:20:09 +0200 Subject: x86/{boot,cpu}: move PIT initialization into cpu_measure_freq --- arch/x86/machine/boot.c | 2 -- arch/x86/machine/cpu.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 5600164..723976c 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -71,7 +71,6 @@ #include #include #include -#include #include #include #include @@ -486,7 +485,6 @@ boot_main(void) syscnt_setup(); percpu_bootstrap(); trap_setup(); - pit_setup_free_running(); cpu_setup(); thread_bootstrap(); console_setup(); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index e9f75a8..846b21e 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -539,6 +539,8 @@ cpu_measure_freq(void) { uint64_t start, end; + pit_setup_free_running(); + start = cpu_get_tsc(); pit_delay(CPU_FREQ_CAL_DELAY); end = cpu_get_tsc(); -- cgit v1.2.3 From c71ab0799e4cc4e5b7de37fc7512a602cf937ff6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 12:59:49 +0200 Subject: x86/ioapic: reduce interrupt override entry size --- arch/x86/machine/ioapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index e49cb30..f0c4d94 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -64,8 +64,8 @@ struct ioapic_map { * Interrupt source override descriptor. */ struct ioapic_iso { - uint8_t source; uint32_t gsi; + uint8_t source; bool active_high; bool edge_triggered; }; -- cgit v1.2.3 From 856176bf91a00c07675cba8407f2bddafbfc27d8 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 13:00:18 +0200 Subject: x86/ioapic: rename interrupts to GSIs where appropriate --- arch/x86/machine/ioapic.c | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index f0c4d94..245c74c 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -76,8 +76,8 @@ struct ioapic { unsigned int apic_id; unsigned int version; volatile struct ioapic_map *map; - unsigned int first_intr; - unsigned int last_intr; + unsigned int first_gsi; + unsigned int last_gsi; }; static unsigned int ioapic_nr_devs; @@ -162,10 +162,10 @@ ioapic_intr(struct trap_frame *frame) } static struct ioapic * __init -ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) +ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int gsi_base) { struct ioapic *ioapic; - unsigned int i, nr_intrs; + unsigned int i, nr_gsis; uint32_t value; ioapic = kmem_alloc(sizeof(*ioapic)); @@ -177,7 +177,7 @@ ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) spinlock_init(&ioapic->lock); ioapic->id = ioapic_nr_devs; ioapic->apic_id = apic_id; - ioapic->first_intr = intr_base; + ioapic->first_gsi = gsi_base; ioapic->map = vm_kmem_map_pa(addr, sizeof(*ioapic->map), NULL, NULL); @@ -188,36 +188,37 @@ ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) value = ioapic_read(ioapic, IOAPIC_REG_VERSION); ioapic->version = (value & IOAPIC_VERSION_VERSION_MASK) >> IOAPIC_VERSION_VERSION_SHIFT; - nr_intrs = ((value & IOAPIC_VERSION_MAXREDIR_MASK) - >> IOAPIC_VERSION_MAXREDIR_SHIFT) + 1; - ioapic->last_intr = ioapic->first_intr + nr_intrs - 1; + nr_gsis = ((value & IOAPIC_VERSION_MAXREDIR_MASK) + >> IOAPIC_VERSION_MAXREDIR_SHIFT) + 1; + ioapic->last_gsi = ioapic->first_gsi + nr_gsis - 1; - if (ioapic->last_intr > (TRAP_INTR_LAST - TRAP_INTR_FIRST)) { + /* XXX This assumes that interrupts are mapped 1:1 to traps */ + if (ioapic->last_gsi > (TRAP_INTR_LAST - TRAP_INTR_FIRST)) { panic("ioapic: invalid interrupt range"); } - for (i = ioapic->first_intr; i < ioapic->last_intr; i++) { + for (i = ioapic->first_gsi; i < ioapic->last_gsi; i++) { trap_register(TRAP_INTR_FIRST + i, ioapic_intr); } - printf("ioapic%u: version:%#x intrs:%u-%u\n", ioapic->id, - ioapic->version, ioapic->first_intr, ioapic->last_intr); + printf("ioapic%u: version:%#x gsis:%u-%u\n", ioapic->id, + ioapic->version, ioapic->first_gsi, ioapic->last_gsi); ioapic_nr_devs++; return ioapic; } static bool -ioapic_has_intr(const struct ioapic *ioapic, unsigned int intr) +ioapic_has_gsi(const struct ioapic *ioapic, unsigned int gsi) { - return ((intr >= ioapic->first_intr) && (intr <= ioapic->last_intr)); + return ((gsi >= ioapic->first_gsi) && (gsi <= ioapic->last_gsi)); } static unsigned int -ioapic_compute_id(const struct ioapic *ioapic, unsigned int intr) +ioapic_compute_id(const struct ioapic *ioapic, unsigned int gsi) { - assert(ioapic_has_intr(ioapic, intr)); - return intr - ioapic->first_intr; + assert(ioapic_has_gsi(ioapic, gsi)); + return gsi - ioapic->first_gsi; } static void @@ -308,13 +309,17 @@ ioapic_setup(void) } void __init -ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int intr_base) +ioapic_register(unsigned int apic_id, uintptr_t addr, unsigned int gsi_base) { struct ioapic *ioapic; - ioapic = ioapic_create(apic_id, addr, intr_base); - intr_register_ctl(&ioapic_ops, ioapic, - ioapic->first_intr, ioapic->last_intr); + ioapic = ioapic_create(apic_id, addr, gsi_base); + + /* + * XXX This assumes that any interrupt override source is included + * in the GSI range. + */ + intr_register_ctl(&ioapic_ops, ioapic, ioapic->first_gsi, ioapic->last_gsi); } void __init -- cgit v1.2.3 From a3f67f9506f917cdb8e56d1860a63fbbcf25fcfa Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 4 Jun 2017 13:16:36 +0200 Subject: kern/arg: fix argument reporting --- kern/arg.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kern/arg.c b/kern/arg.c index 1278812..9c35651 100644 --- a/kern/arg.c +++ b/kern/arg.c @@ -64,7 +64,15 @@ arg_setup(const char *cmdline) void __init arg_info(void) { - printf("arg: %s\n", arg_cmdline); + char cmdline[sizeof(arg_cmdline)]; + size_t i; + + for (i = 0; &arg_cmdline[i] < arg_cmdline_end; i++) { + cmdline[i] = (arg_cmdline[i] == '\0') ? ' ' : arg_cmdline[i]; + } + + cmdline[i] = '\0'; + printf("arg: %s\n", cmdline); } static const char * __init -- cgit v1.2.3 From 1d657d440f090ca1e4514f5c88a50be21815eb41 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 9 Jun 2017 00:23:21 +0200 Subject: kern/work: fix manager wakeup Commit 1b902346ce502870329bda19d92d317a21cab56e introduced a minor regression by also removing a check on the manager of a work pool, which is a pointer to a work_thread, and not directly a thread structure. --- kern/work.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kern/work.c b/kern/work.c index 6c0ed62..5c33eb7 100644 --- a/kern/work.c +++ b/kern/work.c @@ -259,7 +259,9 @@ work_pool_wakeup_manager(struct work_pool *pool) return; } - thread_wakeup(pool->manager->thread); + if (pool->manager != NULL) { + thread_wakeup(pool->manager->thread); + } } static void -- cgit v1.2.3 From 88a92b367618409cf7c6cd50112e3b81f93d5272 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 9 Jun 2017 20:21:41 +0200 Subject: kern/fmt new module This module replaces kern/sprintf, and also implements sscanf functions. --- Makefrag.am | 4 +- include/stdio.h | 10 +- kern/fmt.c | 1430 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/fmt.h | 73 +++ kern/printf.c | 5 +- kern/sprintf.c | 577 ---------------------- kern/sprintf.h | 47 -- 7 files changed, 1516 insertions(+), 630 deletions(-) create mode 100644 kern/fmt.c create mode 100644 kern/fmt.h delete mode 100644 kern/sprintf.c delete mode 100644 kern/sprintf.h diff --git a/Makefrag.am b/Makefrag.am index a9f588e..cab1e95 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -29,6 +29,8 @@ x15_SOURCES += \ kern/error.c \ kern/error.h \ kern/hash.h \ + kern/fmt.c \ + kern/fmt.h \ kern/init.h \ kern/intr.c \ kern/intr.h \ @@ -79,8 +81,6 @@ x15_SOURCES += \ kern/spinlock.h \ kern/spinlock_i.h \ kern/spinlock_types.h \ - kern/sprintf.c \ - kern/sprintf.h \ kern/sref.c \ kern/sref.h \ kern/sref_i.h \ diff --git a/include/stdio.h b/include/stdio.h index e1aa1a3..96c0331 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -18,8 +18,8 @@ #ifndef _STDIO_H #define _STDIO_H +#include #include -#include #ifndef EOF #define EOF (-1) @@ -31,4 +31,12 @@ char console_getchar(void); #define getchar console_getchar #define putchar console_putchar +#define sprintf fmt_sprintf +#define snprintf fmt_snprintf +#define vsprintf fmt_vsprintf +#define vsnprintf fmt_vsnprintf + +#define sscanf fmt_sscanf +#define vsscanf fmt_vsscanf + #endif /* _STDIO_H */ diff --git a/kern/fmt.c b/kern/fmt.c new file mode 100644 index 0000000..06284de --- /dev/null +++ b/kern/fmt.c @@ -0,0 +1,1430 @@ +/* + * Copyright (c) 2010-2017 Richard Braun. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Upstream site with license notes : + * http://git.sceen.net/rbraun/librbraun.git/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Size for the temporary number buffer. The minimum base is 8 so 3 bits + * are consumed per digit. Add one to round up. The conversion algorithm + * doesn't use the null byte. + */ +#define FMT_MAX_NUM_SIZE (((sizeof(unsigned long long) * CHAR_BIT) / 3) + 1) + +/* + * Special size for fmt_vsnprintf(), used when the buffer size is unknown. + */ +#define FMT_NOLIMIT ((size_t)-1) + +/* + * Formatting flags. + * + * FMT_FORMAT_LOWER must be 0x20 as it is OR'd with fmt_digits, eg. + * '0': 0x30 | 0x20 => 0x30 ('0') + * 'A': 0x41 | 0x20 => 0x61 ('a') + */ +#define FMT_FORMAT_ALT_FORM 0x0001 /* "Alternate form" */ +#define FMT_FORMAT_ZERO_PAD 0x0002 /* Zero padding on the left */ +#define FMT_FORMAT_LEFT_JUSTIFY 0x0004 /* Align text on the left */ +#define FMT_FORMAT_BLANK 0x0008 /* Blank space before positive number */ +#define FMT_FORMAT_SIGN 0x0010 /* Always place a sign (either + or -) */ +#define FMT_FORMAT_LOWER 0x0020 /* To lowercase (for %x) */ +#define FMT_FORMAT_CONV_SIGNED 0x0040 /* Format specifies signed conversion */ +#define FMT_FORMAT_DISCARD 0x0080 /* Discard output (scanf) */ +#define FMT_FORMAT_CHECK_WIDTH 0x0100 /* Check field width (scanf) */ + +enum { + FMT_MODIFIER_NONE, + FMT_MODIFIER_CHAR, + FMT_MODIFIER_SHORT, + FMT_MODIFIER_LONG, + FMT_MODIFIER_LONGLONG, + FMT_MODIFIER_PTR, /* Used only for %p */ + FMT_MODIFIER_SIZE, + FMT_MODIFIER_PTRDIFF, +}; + +enum { + FMT_SPECIFIER_INVALID, + FMT_SPECIFIER_INT, + FMT_SPECIFIER_CHAR, + FMT_SPECIFIER_STR, + FMT_SPECIFIER_NRCHARS, + FMT_SPECIFIER_PERCENT, +}; + +/* + * Note that copies of the original va_list object are made, because va_arg() + * may not reliably be used by different callee functions, and despite the + * standard explicitely allowing pointers to va_list objects, it's apparently + * very difficult for implementations to provide and is best avoided. + */ + +struct fmt_sprintf_state { + const char *format; + va_list ap; + unsigned int flags; + int width; + int precision; + unsigned int modifier; + unsigned int specifier; + unsigned int base; + char *str; + char *start; + char *end; +}; + +struct fmt_sscanf_state { + const char *str; + const char *start; + const char *format; + va_list ap; + unsigned int flags; + int width; + int precision; + unsigned int modifier; + unsigned int specifier; + unsigned int base; + int nr_convs; +}; + +static const char fmt_digits[] = "0123456789ABCDEF"; + +static char +fmt_consume(const char **strp) +{ + char c; + + c = **strp; + (*strp)++; + return c; +} + +static void +fmt_restore(const char **strp) +{ + (*strp)--; +} + +static void +fmt_vsnprintf_produce(char **strp, char *end, char c) +{ + if (*strp < end) { + **strp = c; + } + + (*strp)++; +} + +static bool +fmt_isdigit(char c) +{ + return (c >= '0') && (c <= '9'); +} + +static bool +fmt_isxdigit(char c) +{ + return fmt_isdigit(c) + || ((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F')); +} + +static void +fmt_sprintf_state_init(struct fmt_sprintf_state *state, + char *str, size_t size, + const char *format, va_list ap) +{ + state->format = format; + va_copy(state->ap, ap); + state->str = str; + state->start = str; + + if (size == 0) { + state->end = NULL; + } else if (size == FMT_NOLIMIT) { + state->end = (char *)-1; + } else { + state->end = state->start + size - 1; + } +} + +static int +fmt_sprintf_state_finalize(struct fmt_sprintf_state *state) +{ + va_end(state->ap); + + if (state->str < state->end) { + *state->str = '\0'; + } else if (state->end != NULL) { + *state->end = '\0'; + } + + return state->str - state->start; +} + +static char +fmt_sprintf_state_consume_format(struct fmt_sprintf_state *state) +{ + return fmt_consume(&state->format); +} + +static void +fmt_sprintf_state_restore_format(struct fmt_sprintf_state *state) +{ + fmt_restore(&state->format); +} + +static void +fmt_sprintf_state_consume_flags(struct fmt_sprintf_state *state) +{ + bool found; + char c; + + found = true; + state->flags = 0; + + do { + c = fmt_sprintf_state_consume_format(state); + + switch (c) { + case '#': + state->flags |= FMT_FORMAT_ALT_FORM; + break; + case '0': + state->flags |= FMT_FORMAT_ZERO_PAD; + break; + case '-': + state->flags |= FMT_FORMAT_LEFT_JUSTIFY; + break; + case ' ': + state->flags |= FMT_FORMAT_BLANK; + break; + case '+': + state->flags |= FMT_FORMAT_SIGN; + break; + default: + found = false; + break; + } + } while (found); + + fmt_sprintf_state_restore_format(state); +} + +static void +fmt_sprintf_state_consume_width(struct fmt_sprintf_state *state) +{ + char c; + + c = fmt_sprintf_state_consume_format(state); + + if (fmt_isdigit(c)) { + state->width = 0; + + do { + state->width = state->width * 10 + (c - '0'); + c = fmt_sprintf_state_consume_format(state); + } while (fmt_isdigit(c)); + + fmt_sprintf_state_restore_format(state); + } else if (c == '*') { + state->width = va_arg(state->ap, int); + + if (state->width < 0) { + state->flags |= FMT_FORMAT_LEFT_JUSTIFY; + state->width = -state->width; + } + } else { + state->width = 0; + fmt_sprintf_state_restore_format(state); + } +} + +static void +fmt_sprintf_state_consume_precision(struct fmt_sprintf_state *state) +{ + char c; + + c = fmt_sprintf_state_consume_format(state); + + if (c == '.') { + c = fmt_sprintf_state_consume_format(state); + + if (fmt_isdigit(c)) { + state->precision = 0; + + do { + state->precision = state->precision * 10 + (c - '0'); + c = fmt_sprintf_state_consume_format(state); + } while (fmt_isdigit(c)); + + fmt_sprintf_state_restore_format(state); + } else if (c == '*') { + state->precision = va_arg(state->ap, int); + + if (state->precision < 0) { + state->precision = 0; + } + } else { + state->precision = 0; + fmt_sprintf_state_restore_format(state); + } + } else { + /* precision is >= 0 only if explicit */ + state->precision = -1; + fmt_sprintf_state_restore_format(state); + } +} + +static void +fmt_sprintf_state_consume_modifier(struct fmt_sprintf_state *state) +{ + char c, c2; + + c = fmt_sprintf_state_consume_format(state); + + switch (c) { + case 'h': + case 'l': + c2 = fmt_sprintf_state_consume_format(state); + + if (c == c2) { + state->modifier = (c == 'h') ? FMT_MODIFIER_CHAR + : FMT_MODIFIER_LONGLONG; + } else { + state->modifier = (c == 'h') ? FMT_MODIFIER_SHORT + : FMT_MODIFIER_LONG; + fmt_sprintf_state_restore_format(state); + } + + break; + case 'z': + state->modifier = FMT_MODIFIER_SIZE; + case 't': + state->modifier = FMT_MODIFIER_PTRDIFF; + break; + default: + state->modifier = FMT_MODIFIER_NONE; + fmt_sprintf_state_restore_format(state); + break; + } +} + +static void +fmt_sprintf_state_consume_specifier(struct fmt_sprintf_state *state) +{ + char c; + + c = fmt_sprintf_state_consume_format(state); + + switch (c) { + case 'd': + case 'i': + state->flags |= FMT_FORMAT_CONV_SIGNED; + case 'u': + state->base = 10; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'o': + state->base = 8; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'p': + state->flags |= FMT_FORMAT_ALT_FORM; + state->modifier = FMT_MODIFIER_PTR; + case 'x': + state->flags |= FMT_FORMAT_LOWER; + case 'X': + state->base = 16; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'c': + state->specifier = FMT_SPECIFIER_CHAR; + break; + case 's': + state->specifier = FMT_SPECIFIER_STR; + break; + case 'n': + state->specifier = FMT_SPECIFIER_NRCHARS; + break; + case '%': + state->specifier = FMT_SPECIFIER_PERCENT; + break; + default: + state->specifier = FMT_SPECIFIER_INVALID; + fmt_sprintf_state_restore_format(state); + break; + } +} + +static void +fmt_sprintf_state_produce_raw_char(struct fmt_sprintf_state *state, char c) +{ + fmt_vsnprintf_produce(&state->str, state->end, c); +} + +static int +fmt_sprintf_state_consume(struct fmt_sprintf_state *state) +{ + char c; + + c = fmt_consume(&state->format); + + if (c == '\0') { + return ERROR_IO; + } + + if (c != '%') { + fmt_sprintf_state_produce_raw_char(state, c); + return ERROR_AGAIN; + } + + fmt_sprintf_state_consume_flags(state); + fmt_sprintf_state_consume_width(state); + fmt_sprintf_state_consume_precision(state); + fmt_sprintf_state_consume_modifier(state); + fmt_sprintf_state_consume_specifier(state); + return 0; +} + +static void +fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) +{ + char c, sign, tmp[FMT_MAX_NUM_SIZE]; + unsigned int r, mask, shift; + unsigned long long n; + int i; + + switch (state->modifier) { + case FMT_MODIFIER_CHAR: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = (signed char)va_arg(state->ap, int); + } else { + n = (unsigned char)va_arg(state->ap, int); + } + + break; + case FMT_MODIFIER_SHORT: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = (short)va_arg(state->ap, int); + } else { + n = (unsigned short)va_arg(state->ap, int); + } + + break; + case FMT_MODIFIER_LONG: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = va_arg(state->ap, long); + } else { + n = va_arg(state->ap, unsigned long); + } + + break; + case FMT_MODIFIER_LONGLONG: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = va_arg(state->ap, long long); + } else { + n = va_arg(state->ap, unsigned long long); + } + + break; + case FMT_MODIFIER_PTR: + n = (uintptr_t)va_arg(state->ap, void *); + break; + case FMT_MODIFIER_SIZE: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = va_arg(state->ap, ssize_t); + } else { + n = va_arg(state->ap, size_t); + } + + break; + case FMT_MODIFIER_PTRDIFF: + n = va_arg(state->ap, ptrdiff_t); + break; + default: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + n = va_arg(state->ap, int); + } else { + n = va_arg(state->ap, unsigned int); + } + + break; + } + + if ((state->flags & FMT_FORMAT_LEFT_JUSTIFY) || (state->precision >= 0)) { + state->flags &= ~FMT_FORMAT_ZERO_PAD; + } + + sign = '\0'; + + if (state->flags & FMT_FORMAT_ALT_FORM) { + /* '0' for octal */ + state->width--; + + /* '0x' or '0X' for hexadecimal */ + if (state->base == 16) { + state->width--; + } + } else if (state->flags & FMT_FORMAT_CONV_SIGNED) { + if ((long long)n < 0) { + sign = '-'; + state->width--; + n = -(long long)n; + } else if (state->flags & FMT_FORMAT_SIGN) { + /* FMT_FORMAT_SIGN must precede FMT_FORMAT_BLANK. */ + sign = '+'; + state->width--; + } else if (state->flags & FMT_FORMAT_BLANK) { + sign = ' '; + state->width--; + } + } + + /* Conversion, in reverse order */ + + i = 0; + + if (n == 0) { + if (state->precision != 0) { + tmp[i++] = '0'; + } + } else if (state->base == 10) { + /* + * Try to avoid 64 bits operations if the processor doesn't + * support them. Note that even when using modulus and + * division operators close to each other, the compiler may + * forge two functions calls to compute the quotient and the + * remainder, whereas processor instructions are generally + * correctly used once, giving both results at once, through + * plain or reciprocal division. + */ +#ifndef __LP64__ + if (state->modifier == FMT_MODIFIER_LONGLONG) { +#endif /* __LP64__ */ + do { + r = n % 10; + n /= 10; + tmp[i++] = fmt_digits[r]; + } while (n != 0); +#ifndef __LP64__ + } else { + unsigned long m; + + m = (unsigned long)n; + + do { + r = m % 10; + m /= 10; + tmp[i++] = fmt_digits[r]; + } while (m != 0); + } +#endif /* __LP64__ */ + } else { + mask = state->base - 1; + shift = (state->base == 8) ? 3 : 4; + + do { + r = n & mask; + n >>= shift; + tmp[i++] = fmt_digits[r] | (state->flags & FMT_FORMAT_LOWER); + } while (n != 0); + } + + if (i > state->precision) { + state->precision = i; + } + + state->width -= state->precision; + + if (!(state->flags & (FMT_FORMAT_LEFT_JUSTIFY | FMT_FORMAT_ZERO_PAD))) { + while (state->width-- > 0) { + fmt_sprintf_state_produce_raw_char(state, ' '); + } + } + + if (state->flags & FMT_FORMAT_ALT_FORM) { + fmt_sprintf_state_produce_raw_char(state, '0'); + + if (state->base == 16) { + c = 'X' | (state->flags & FMT_FORMAT_LOWER); + fmt_sprintf_state_produce_raw_char(state, c); + } + } else if (sign != '\0') { + fmt_sprintf_state_produce_raw_char(state, sign); + } + + if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { + c = (state->flags & FMT_FORMAT_ZERO_PAD) ? '0' : ' '; + + while (state->width-- > 0) { + fmt_sprintf_state_produce_raw_char(state, c); + } + } + + while (i < state->precision--) { + fmt_sprintf_state_produce_raw_char(state, '0'); + } + + while (i-- > 0) { + fmt_sprintf_state_produce_raw_char(state, tmp[i]); + } + + while (state->width-- > 0) { + fmt_sprintf_state_produce_raw_char(state, ' '); + } +} + +static void +fmt_sprintf_state_produce_char(struct fmt_sprintf_state *state) +{ + char c; + + c = va_arg(state->ap, int); + + if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { + while (--state->width > 0) { + fmt_sprintf_state_produce_raw_char(state, ' '); + } + } + + fmt_sprintf_state_produce_raw_char(state, c); + + while (--state->width > 0) { + fmt_sprintf_state_produce_raw_char(state, ' '); + } +} + +static void +fmt_sprintf_state_produce_str(struct fmt_sprintf_state *state) +{ + int i, len; + char *s; + + s = va_arg(state->ap, char *); + + if (s == NULL) { + s = "(null)"; + } + + len = 0; + + for (len = 0; s[len] != '\0'; len++) + if (len == state->precision) { + break; + } + + if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { + while (len < state->width) { + state->width--; + fmt_sprintf_state_produce_raw_char(state, ' '); + } + } + + for (i = 0; i < len; i++) { + fmt_sprintf_state_produce_raw_char(state, *s); + s++; + } + + while (len < state->width) { + state->width--; + fmt_sprintf_state_produce_raw_char(state, ' '); + } +} + +static void +fmt_sprintf_state_produce_nrchars(struct fmt_sprintf_state *state) +{ + if (state->modifier == FMT_MODIFIER_CHAR) { + signed char *ptr = va_arg(state->ap, signed char *); + *ptr = state->str - state->start; + } else if (state->modifier == FMT_MODIFIER_SHORT) { + short *ptr = va_arg(state->ap, short *); + *ptr = state->str - state->start; + } else if (state->modifier == FMT_MODIFIER_LONG) { + long *ptr = va_arg(state->ap, long *); + *ptr = state->str - state->start; + } else if (state->modifier == FMT_MODIFIER_LONGLONG) { + long long *ptr = va_arg(state->ap, long long *); + *ptr = state->str - state->start; + } else if (state->modifier == FMT_MODIFIER_SIZE) { + ssize_t *ptr = va_arg(state->ap, ssize_t *); + *ptr = state->str - state->start; + } else if (state->modifier == FMT_MODIFIER_PTRDIFF) { + ptrdiff_t *ptr = va_arg(state->ap, ptrdiff_t *); + *ptr = state->str - state->start; + } else { + int *ptr = va_arg(state->ap, int *); + *ptr = state->str - state->start; + } +} + +static void +fmt_sprintf_state_produce(struct fmt_sprintf_state *state) +{ + switch (state->specifier) { + case FMT_SPECIFIER_INT: + fmt_sprintf_state_produce_int(state); + break; + case FMT_SPECIFIER_CHAR: + fmt_sprintf_state_produce_char(state); + break; + case FMT_SPECIFIER_STR: + fmt_sprintf_state_produce_str(state); + break; + case FMT_SPECIFIER_NRCHARS: + fmt_sprintf_state_produce_nrchars(state); + break; + case FMT_SPECIFIER_PERCENT: + case FMT_SPECIFIER_INVALID: + fmt_sprintf_state_produce_raw_char(state, '%'); + break; + } +} + +int +fmt_sprintf(char *str, const char *format, ...) +{ + va_list ap; + int length; + + va_start(ap, format); + length = fmt_vsprintf(str, format, ap); + va_end(ap); + + return length; +} + +int +fmt_vsprintf(char *str, const char *format, va_list ap) +{ + return fmt_vsnprintf(str, FMT_NOLIMIT, format, ap); +} + +int +fmt_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int length; + + va_start(ap, format); + length = fmt_vsnprintf(str, size, format, ap); + va_end(ap); + + return length; +} + +int +fmt_vsnprintf(char *str, size_t size, const char *format, va_list ap) +{ + struct fmt_sprintf_state state; + int error; + + fmt_sprintf_state_init(&state, str, size, format, ap); + + for (;;) { + error = fmt_sprintf_state_consume(&state); + + if (error == ERROR_AGAIN) { + continue; + } else if (error) { + break; + } + + fmt_sprintf_state_produce(&state); + } + + return fmt_sprintf_state_finalize(&state); +} + +static char +fmt_atoi(char c) +{ + assert(fmt_isxdigit(c)); + + if (fmt_isdigit(c)) { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return 10 + (c - 'a'); + } else { + return 10 + (c - 'A'); + } +} + +static bool +fmt_isspace(char c) +{ + if (c == ' ') { + return true; + } + + if ((c >= '\t') && (c <= '\f')) { + return true; + } + + return false; +} + +static void +fmt_skip(const char **strp) +{ + while (fmt_isspace(**strp)) { + (*strp)++; + } +} + +static void +fmt_sscanf_state_init(struct fmt_sscanf_state *state, const char *str, + const char *format, va_list ap) +{ + state->str = str; + state->start = str; + state->format = format; + state->flags = 0; + state->width = 0; + va_copy(state->ap, ap); + state->nr_convs = 0; +} + +static int +fmt_sscanf_state_finalize(struct fmt_sscanf_state *state) +{ + va_end(state->ap); + return state->nr_convs; +} + +static void +fmt_sscanf_state_report_conv(struct fmt_sscanf_state *state) +{ + if (state->nr_convs == EOF) { + state->nr_convs = 1; + return; + } + + state->nr_convs++; +} + +static void +fmt_sscanf_state_report_error(struct fmt_sscanf_state *state) +{ + if (state->nr_convs != 0) { + return; + } + + state->nr_convs = EOF; +} + +static void +fmt_sscanf_state_skip_space(struct fmt_sscanf_state *state) +{ + fmt_skip(&state->str); +} + +static char +fmt_sscanf_state_consume_string(struct fmt_sscanf_state *state) +{ + char c; + + c = fmt_consume(&state->str); + + if (state->flags & FMT_FORMAT_CHECK_WIDTH) { + if (state->width == 0) { + c = EOF; + } else { + state->width--; + } + } + + return c; +} + +static void +fmt_sscanf_state_restore_string(struct fmt_sscanf_state *state) +{ + fmt_restore(&state->str); +} + +static char +fmt_sscanf_state_consume_format(struct fmt_sscanf_state *state) +{ + return fmt_consume(&state->format); +} + +static void +fmt_sscanf_state_restore_format(struct fmt_sscanf_state *state) +{ + fmt_restore(&state->format); +} + +static void +fmt_sscanf_state_consume_flags(struct fmt_sscanf_state *state) +{ + bool found; + char c; + + found = true; + state->flags = 0; + + do { + c = fmt_sscanf_state_consume_format(state); + + switch (c) { + case '*': + state->flags |= FMT_FORMAT_DISCARD; + break; + default: + found = false; + break; + } + } while (found); + + fmt_sscanf_state_restore_format(state); +} + +static void +fmt_sscanf_state_consume_width(struct fmt_sscanf_state *state) +{ + char c; + + state->width = 0; + + for (;;) { + c = fmt_sscanf_state_consume_format(state); + + if (!fmt_isdigit(c)) { + break; + } + + state->width = state->width * 10 + (c - '0'); + } + + if (state->width != 0) { + state->flags |= FMT_FORMAT_CHECK_WIDTH; + } + + fmt_sscanf_state_restore_format(state); +} + +static void +fmt_sscanf_state_consume_modifier(struct fmt_sscanf_state *state) +{ + char c, c2; + + c = fmt_sscanf_state_consume_format(state); + + switch (c) { + case 'h': + case 'l': + c2 = fmt_sscanf_state_consume_format(state); + + if (c == c2) { + state->modifier = (c == 'h') ? FMT_MODIFIER_CHAR + : FMT_MODIFIER_LONGLONG; + } else { + state->modifier = (c == 'h') ? FMT_MODIFIER_SHORT + : FMT_MODIFIER_LONG; + fmt_sscanf_state_restore_format(state); + } + + break; + case 'z': + state->modifier = FMT_MODIFIER_SIZE; + break; + case 't': + state->modifier = FMT_MODIFIER_PTRDIFF; + break; + default: + state->modifier = FMT_MODIFIER_NONE; + fmt_sscanf_state_restore_format(state); + break; + } +} + +static void +fmt_sscanf_state_consume_specifier(struct fmt_sscanf_state *state) +{ + char c; + + c = fmt_sscanf_state_consume_format(state); + + switch (c) { + case 'i': + state->base = 0; + state->flags |= FMT_FORMAT_CONV_SIGNED; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'd': + state->flags |= FMT_FORMAT_CONV_SIGNED; + case 'u': + state->base = 10; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'o': + state->base = 8; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'p': + state->modifier = FMT_MODIFIER_PTR; + case 'x': + case 'X': + state->base = 16; + state->specifier = FMT_SPECIFIER_INT; + break; + case 'c': + state->specifier = FMT_SPECIFIER_CHAR; + break; + case 's': + state->specifier = FMT_SPECIFIER_STR; + break; + case 'n': + state->specifier = FMT_SPECIFIER_NRCHARS; + break; + case '%': + state->specifier = FMT_SPECIFIER_PERCENT; + break; + default: + state->specifier = FMT_SPECIFIER_INVALID; + fmt_sscanf_state_restore_format(state); + break; + } +} + +static int +fmt_sscanf_state_discard_char(struct fmt_sscanf_state *state, char c) +{ + char c2; + + if (fmt_isspace(c)) { + fmt_sscanf_state_skip_space(state); + return 0; + } + + c2 = fmt_sscanf_state_consume_string(state); + + if (c != c2) { + if ((c2 == '\0') && (state->nr_convs == 0)) { + state->nr_convs = EOF; + } + + return ERROR_INVAL; + } + + return 0; +} + +static int +fmt_sscanf_state_consume(struct fmt_sscanf_state *state) +{ + int error; + char c; + + state->flags = 0; + + c = fmt_consume(&state->format); + + if (c == '\0') { + return ERROR_IO; + } + + if (c != '%') { + error = fmt_sscanf_state_discard_char(state, c); + + if (error) { + return error; + } + + return ERROR_AGAIN; + } + + fmt_sscanf_state_consume_flags(state); + fmt_sscanf_state_consume_width(state); + fmt_sscanf_state_consume_modifier(state); + fmt_sscanf_state_consume_specifier(state); + return 0; +} + +static int +fmt_sscanf_state_produce_int(struct fmt_sscanf_state *state) +{ + unsigned long long n, m, tmp; + char c, buf[FMT_MAX_NUM_SIZE]; + bool negative; + size_t i; + + negative = 0; + + fmt_sscanf_state_skip_space(state); + c = fmt_sscanf_state_consume_string(state); + + if (c == '-') { + negative = true; + c = fmt_sscanf_state_consume_string(state); + } + + if (c == '0') { + c = fmt_sscanf_state_consume_string(state); + + if ((c == 'x') || (c == 'X')) { + if (state->base == 0) { + state->base = 16; + } + + if (state->base == 16) { + c = fmt_sscanf_state_consume_string(state); + } else { + fmt_sscanf_state_restore_string(state); + } + } else { + if (state->base == 0) { + state->base = 8; + } + } + } + + i = 0; + + while (c != '\0') { + if (state->base == 8) { + if (!((c >= '0') && (c <= '7'))) { + break; + } + } else if (state->base == 16) { + if (!fmt_isxdigit(c)) { + break; + } + } else { + if (!fmt_isdigit(c)) { + break; + } + } + + /* XXX Standard sscanf provides no way to cleanly handle overflows */ + if (i < (ARRAY_SIZE(buf) - 1)) { + buf[i] = c; + } else if (i == (ARRAY_SIZE(buf) - 1)) { + strcpy(buf, "1"); + negative = true; + } + + i++; + c = fmt_sscanf_state_consume_string(state); + } + + fmt_sscanf_state_restore_string(state); + + if (state->flags & FMT_FORMAT_DISCARD) { + return 0; + } + + if (i == 0) { + if (c == '\0') { + fmt_sscanf_state_report_error(state); + return ERROR_INVAL; + } + + buf[0] = '0'; + i = 1; + } + + if (i < ARRAY_SIZE(buf)) { + buf[i] = '\0'; + i--; + } else { + i = strlen(buf) - 1; + } + + n = 0; + +#ifndef __LP64__ + if (state->modifier == FMT_MODIFIER_LONGLONG) { +#endif /* __LP64__ */ + m = 1; + tmp = 0; + + while (&buf[i] >= buf) { + tmp += fmt_atoi(buf[i]) * m; + + if (tmp < n) { + n = 1; + negative = true; + break; + } + + n = tmp; + m *= state->base; + i--; + } +#ifndef __LP64__ + } else { + unsigned long _n, _m, _tmp; + + _n = 0; + _m = 1; + _tmp = 0; + + while (&buf[i] >= buf) { + _tmp += fmt_atoi(buf[i]) * _m; + + if (_tmp < _n) { + _n = 1; + negative = true; + break; + } + + _n = _tmp; + _m *= state->base; + i--; + } + + n = _n; + } +#endif /* __LP64__ */ + + if (negative) { + n = -n; + } + + switch (state->modifier) { + case FMT_MODIFIER_CHAR: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + *va_arg(state->ap, char *) = n; + } else { + *va_arg(state->ap, unsigned char *) = n; + } + + break; + case FMT_MODIFIER_SHORT: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + *va_arg(state->ap, short *) = n; + } else { + *va_arg(state->ap, unsigned short *) = n; + } + + break; + case FMT_MODIFIER_LONG: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + *va_arg(state->ap, long *) = n; + } else { + *va_arg(state->ap, unsigned long *) = n; + } + + break; + case FMT_MODIFIER_LONGLONG: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + *va_arg(state->ap, long long *) = n; + } else { + *va_arg(state->ap, unsigned long long *) = n; + } + + break; + case FMT_MODIFIER_PTR: + *va_arg(state->ap, uintptr_t *) = n; + break; + case FMT_MODIFIER_SIZE: + *va_arg(state->ap, size_t *) = n; + break; + case FMT_MODIFIER_PTRDIFF: + *va_arg(state->ap, ptrdiff_t *) = n; + break; + default: + if (state->flags & FMT_FORMAT_CONV_SIGNED) { + *va_arg(state->ap, int *) = n; + } else { + *va_arg(state->ap, unsigned int *) = n; + } + } + + fmt_sscanf_state_report_conv(state); + return 0; +} + +static int +fmt_sscanf_state_produce_char(struct fmt_sscanf_state *state) +{ + char c, *dest; + int i, width; + + if (state->flags & FMT_FORMAT_DISCARD) { + dest = NULL; + } else { + dest = va_arg(state->ap, char *); + } + + if (state->flags & FMT_FORMAT_CHECK_WIDTH) { + width = state->width; + } else { + width = 1; + } + + for (i = 0; i < width; i++) { + c = fmt_sscanf_state_consume_string(state); + + if ((c == '\0') || (c == EOF)) { + break; + } + + if (dest != NULL) { + *dest = c; + dest++; + } + } + + if (i < width) { + fmt_sscanf_state_restore_string(state); + } + + if (dest != NULL) { + fmt_sscanf_state_report_conv(state); + } + + return 0; +} + +static int +fmt_sscanf_state_produce_str(struct fmt_sscanf_state *state) +{ + const char *orig; + char c, *dest; + + orig = state->str; + + fmt_sscanf_state_skip_space(state); + + if (state->flags & FMT_FORMAT_DISCARD) { + dest = NULL; + } else { + dest = va_arg(state->ap, char *); + } + + for (;;) { + c = fmt_sscanf_state_consume_string(state); + + if ((c == '\0') || (c == ' ') || (c == EOF)) { + break; + } + + if (dest != NULL) { + *dest = c; + dest++; + } + } + + fmt_sscanf_state_restore_string(state); + + if (state->str == orig) { + fmt_sscanf_state_report_error(state); + return ERROR_INVAL; + } + + if (dest != NULL) { + *dest = '\0'; + fmt_sscanf_state_report_conv(state); + } + + return 0; +} + +static int +fmt_sscanf_state_produce_nrchars(struct fmt_sscanf_state *state) +{ + *va_arg(state->ap, int *) = state->str - state->start; + return 0; +} + +static int +fmt_sscanf_state_produce(struct fmt_sscanf_state *state) +{ + switch (state->specifier) { + case FMT_SPECIFIER_INT: + return fmt_sscanf_state_produce_int(state); + case FMT_SPECIFIER_CHAR: + return fmt_sscanf_state_produce_char(state); + case FMT_SPECIFIER_STR: + return fmt_sscanf_state_produce_str(state); + case FMT_SPECIFIER_NRCHARS: + return fmt_sscanf_state_produce_nrchars(state); + case FMT_SPECIFIER_PERCENT: + fmt_sscanf_state_skip_space(state); + return fmt_sscanf_state_discard_char(state, '%'); + default: + fmt_sscanf_state_report_error(state); + return ERROR_INVAL; + } +} + +int +fmt_sscanf(const char *str, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = fmt_vsscanf(str, format, ap); + va_end(ap); + + return ret; +} + +int +fmt_vsscanf(const char *str, const char *format, va_list ap) +{ + struct fmt_sscanf_state state; + int error; + + fmt_sscanf_state_init(&state, str, format, ap); + + for (;;) { + error = fmt_sscanf_state_consume(&state); + + if (error == ERROR_AGAIN) { + continue; + } else if (error) { + break; + } + + error = fmt_sscanf_state_produce(&state); + + if (error) { + break; + } + } + + return fmt_sscanf_state_finalize(&state); +} diff --git a/kern/fmt.h b/kern/fmt.h new file mode 100644 index 0000000..a339ed4 --- /dev/null +++ b/kern/fmt.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2017 Richard Braun. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Formatted string functions. + * + * The functions provided by this module implement a subset of the standard + * sprintf- and sscanf-like functions. + * + * sprintf: + * - flags: # 0 - ' ' (space) + + * - field width is supported + * - precision is supported + * + * sscanf: + * - flags: * + * - field width is supported + * + * common: + * - modifiers: hh h l ll z t + * - specifiers: d i o u x X c s p n % + * + * + * Upstream site with license notes : + * http://git.sceen.net/rbraun/librbraun.git/ + */ + +#ifndef _FMT_H +#define _FMT_H + +#include +#include + +int fmt_sprintf(char *str, const char *format, ...) + __attribute__((format(printf, 2, 3))); + +int fmt_vsprintf(char *str, const char *format, va_list ap) + __attribute__((format(printf, 2, 0))); + +int fmt_snprintf(char *str, size_t size, const char *format, ...) + __attribute__((format(printf, 3, 4))); + +int fmt_vsnprintf(char *str, size_t size, const char *format, va_list ap) + __attribute__((format(printf, 3, 0))); + +int fmt_sscanf(const char *str, const char *format, ...) + __attribute__((format(scanf, 2, 3))); + +int fmt_vsscanf(const char *str, const char *format, va_list ap) + __attribute__((format(scanf, 2, 0))); + +#endif /* _FMT_H */ diff --git a/kern/printf.c b/kern/printf.c index f657fdf..64324a0 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -15,9 +15,8 @@ * along with this program. If not, see . */ -#include - #include +#include #include #include @@ -51,7 +50,7 @@ vprintf(const char *format, va_list ap) spinlock_lock_intr_save(&printf_lock, &flags); - length = vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); + length = fmt_vsnprintf(printf_buffer, sizeof(printf_buffer), format, ap); for (ptr = printf_buffer; *ptr != '\0'; ptr++) { console_putchar(*ptr); diff --git a/kern/sprintf.c b/kern/sprintf.c deleted file mode 100644 index f6346e9..0000000 --- a/kern/sprintf.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (c) 2010 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 3 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 - -/* - * Formatting flags. - * - * FORMAT_LOWER must be 0x20 as it is OR'd with digits, eg. - * '0': 0x30 | 0x20 => 0x30 ('0') - * 'A': 0x41 | 0x20 => 0x61 ('a') - */ -#define SPRINTF_FORMAT_ALT_FORM 0x01 -#define SPRINTF_FORMAT_ZERO_PAD 0x02 -#define SPRINTF_FORMAT_LEFT_JUSTIFY 0x04 -#define SPRINTF_FORMAT_BLANK 0x08 -#define SPRINTF_FORMAT_SIGN 0x10 -#define SPRINTF_FORMAT_LOWER 0x20 -#define SPRINTF_FORMAT_CONV_SIGNED 0x40 - -enum { - SPRINTF_MODIFIER_NONE, - SPRINTF_MODIFIER_CHAR, - SPRINTF_MODIFIER_SHORT, - SPRINTF_MODIFIER_LONG, - SPRINTF_MODIFIER_LONGLONG, - SPRINTF_MODIFIER_PTR, /* Used only for %p */ - SPRINTF_MODIFIER_SIZE, - SPRINTF_MODIFIER_PTRDIFF -}; - -enum { - SPRINTF_SPECIFIER_INVALID, - SPRINTF_SPECIFIER_INT, - SPRINTF_SPECIFIER_CHAR, - SPRINTF_SPECIFIER_STR, - SPRINTF_SPECIFIER_NRCHARS, - SPRINTF_SPECIFIER_PERCENT -}; - -/* - * Size for the temporary number buffer. The minimum base is 8 so 3 bits - * are consumed per digit. Add one to round up. The conversion algorithm - * doesn't use the null byte. - */ -#define SPRINTF_MAX_NUM_SIZE (((sizeof(uint64_t) * CHAR_BIT) / 3) + 1) - -/* - * Special size for vsnprintf(), used by sprintf()/vsprintf() when the - * buffer size is unknown. - */ -#define SPRINTF_NOLIMIT ((size_t)-1) - -static const char sprintf_digits[] = "0123456789ABCDEF"; - -static inline char * -sprintf_putchar(char *str, char *end, char c) -{ - if (str < end) { - *str = c; - } - - str++; - - return str; -} - -static inline int -sprintf_isdigit(char c) -{ - return (c >= '0') && (c <= '9'); -} - -int -sprintf(char *str, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = vsprintf(str, format, ap); - va_end(ap); - - return length; -} - -int -vsprintf(char *str, const char *format, va_list ap) -{ - return vsnprintf(str, SPRINTF_NOLIMIT, format, ap); -} - -int -snprintf(char *str, size_t size, const char *format, ...) -{ - va_list ap; - int length; - - va_start(ap, format); - length = vsnprintf(str, size, format, ap); - va_end(ap); - - return length; -} - -int -vsnprintf(char *str, size_t size, const char *format, va_list ap) -{ - unsigned long long n; - int i, len, found, flags, width, precision, modifier, specifier, shift; - unsigned char r, base, mask; - char c, *s, *start, *end, sign, tmp[SPRINTF_MAX_NUM_SIZE]; - - start = str; - - if (size == 0) { - end = NULL; - } else if (size == SPRINTF_NOLIMIT) { - end = (char *)-1; - } else { - end = start + size - 1; - } - - while ((c = *format) != '\0') { - if (c != '%') { - str = sprintf_putchar(str, end, c); - format++; - continue; - } - - /* Flags */ - - found = 1; - flags = 0; - - do { - format++; - c = *format; - - switch (c) { - case '#': - flags |= SPRINTF_FORMAT_ALT_FORM; - break; - case '0': - flags |= SPRINTF_FORMAT_ZERO_PAD; - break; - case '-': - flags |= SPRINTF_FORMAT_LEFT_JUSTIFY; - break; - case ' ': - flags |= SPRINTF_FORMAT_BLANK; - break; - case '+': - flags |= SPRINTF_FORMAT_SIGN; - break; - default: - found = 0; - break; - } - } while (found); - - /* Width */ - - if (sprintf_isdigit(c)) { - width = 0; - - while (sprintf_isdigit(c)) { - width = width * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - width = va_arg(ap, int); - - if (width < 0) { - flags |= SPRINTF_FORMAT_LEFT_JUSTIFY; - width = -width; - } - - format++; - c = *format; - } else { - width = 0; - } - - /* Precision */ - - if (c == '.') { - format++; - c = *format; - - if (sprintf_isdigit(c)) { - precision = 0; - - while (sprintf_isdigit(c)) { - precision = precision * 10 + (c - '0'); - format++; - c = *format; - } - } else if (c == '*') { - precision = va_arg(ap, int); - - if (precision < 0) { - precision = 0; - } - - format++; - c = *format; - } else { - precision = 0; - } - } else { - /* precision is >= 0 only if explicit */ - precision = -1; - } - - /* Length modifier */ - - switch (c) { - case 'h': - case 'l': - format++; - - if (c == *format) { - modifier = (c == 'h') - ? SPRINTF_MODIFIER_CHAR - : SPRINTF_MODIFIER_LONGLONG; - goto skip_modifier; - } else { - modifier = (c == 'h') - ? SPRINTF_MODIFIER_SHORT - : SPRINTF_MODIFIER_LONG; - c = *format; - } - - break; - case 'z': - modifier = SPRINTF_MODIFIER_SIZE; - goto skip_modifier; - case 't': - modifier = SPRINTF_MODIFIER_PTRDIFF; -skip_modifier: - format++; - c = *format; - break; - default: - modifier = SPRINTF_MODIFIER_NONE; - break; - } - - /* Specifier */ - - switch (c) { - case 'd': - case 'i': - flags |= SPRINTF_FORMAT_CONV_SIGNED; - case 'u': - base = 10; - goto integer; - case 'o': - base = 8; - goto integer; - case 'p': - flags |= SPRINTF_FORMAT_ALT_FORM; - modifier = SPRINTF_MODIFIER_PTR; - case 'x': - flags |= SPRINTF_FORMAT_LOWER; - case 'X': - base = 16; -integer: - specifier = SPRINTF_SPECIFIER_INT; - break; - case 'c': - specifier = SPRINTF_SPECIFIER_CHAR; - break; - case 's': - specifier = SPRINTF_SPECIFIER_STR; - break; - case 'n': - specifier = SPRINTF_SPECIFIER_NRCHARS; - break; - case '%': - specifier = SPRINTF_SPECIFIER_PERCENT; - break; - default: - specifier = SPRINTF_SPECIFIER_INVALID; - break; - } - - /* Output */ - - switch (specifier) { - case SPRINTF_SPECIFIER_INT: - switch (modifier) { - case SPRINTF_MODIFIER_CHAR: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = (signed char)va_arg(ap, int); - } else { - n = (unsigned char)va_arg(ap, int); - } - break; - case SPRINTF_MODIFIER_SHORT: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = (short)va_arg(ap, int); - } else { - n = (unsigned short)va_arg(ap, int); - } - break; - case SPRINTF_MODIFIER_LONG: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = va_arg(ap, long); - } else { - n = va_arg(ap, unsigned long); - } - break; - case SPRINTF_MODIFIER_LONGLONG: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = va_arg(ap, long long); - } else { - n = va_arg(ap, unsigned long long); - } - break; - case SPRINTF_MODIFIER_PTR: - n = (uintptr_t)va_arg(ap, void *); - break; - case SPRINTF_MODIFIER_SIZE: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = va_arg(ap, ssize_t); - } else { - n = va_arg(ap, size_t); - } - break; - case SPRINTF_MODIFIER_PTRDIFF: - n = va_arg(ap, ptrdiff_t); - break; - default: - if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - n = va_arg(ap, int); - } else { - n = va_arg(ap, unsigned int); - } - break; - } - - if ((flags & SPRINTF_FORMAT_LEFT_JUSTIFY) || (precision >= 0)) { - flags &= ~SPRINTF_FORMAT_ZERO_PAD; - } - - sign = 0; - - if (flags & SPRINTF_FORMAT_ALT_FORM) { - /* '0' for octal */ - width--; - - /* '0x' or '0X' for hexadecimal */ - if (base == 16) { - width--; - } - } else if (flags & SPRINTF_FORMAT_CONV_SIGNED) { - if ((long long)n < 0) { - sign = '-'; - width--; - n = -(long long)n; - } else if (flags & SPRINTF_FORMAT_SIGN) { - /* SPRINTF_FORMAT_SIGN must precede SPRINTF_FORMAT_BLANK. */ - sign = '+'; - width--; - } else if (flags & SPRINTF_FORMAT_BLANK) { - sign = ' '; - width--; - } - } - - /* Conversion, in reverse order */ - - i = 0; - - if (n == 0) { - if (precision != 0) { - tmp[i++] = '0'; - } - } else if (base == 10) { - /* - * Try to avoid 64 bits operations if the processor doesn't - * support them. Note that even when using modulus and - * division operators close to each other, the compiler will - * forge two calls to __udivdi3() and __umoddi3() instead of - * one to __udivmoddi3(), whereas processor instructions are - * generally correctly used once, giving both the remainder - * and the quotient, through plain or reciprocal division. - */ -#ifndef __LP64__ - if (modifier == SPRINTF_MODIFIER_LONGLONG) { -#endif /* __LP64__ */ - do { - r = n % 10; - n /= 10; - tmp[i++] = sprintf_digits[r]; - } while (n != 0); -#ifndef __LP64__ - } else { - unsigned long m; - - m = (unsigned long)n; - - do { - r = m % 10; - m /= 10; - tmp[i++] = sprintf_digits[r]; - } while (m != 0); - } -#endif /* __LP64__ */ - } else { - mask = base - 1; - shift = (base == 8) ? 3 : 4; - - do { - r = (unsigned char)n & mask; - n >>= shift; - tmp[i++] = sprintf_digits[r] - | (flags & SPRINTF_FORMAT_LOWER); - } while (n != 0); - } - - if (i > precision) { - precision = i; - } - - width -= precision; - - if (!(flags & (SPRINTF_FORMAT_LEFT_JUSTIFY - | SPRINTF_FORMAT_ZERO_PAD))) - while (width-- > 0) { - str = sprintf_putchar(str, end, ' '); - } - - if (flags & SPRINTF_FORMAT_ALT_FORM) { - str = sprintf_putchar(str, end, '0'); - - if (base == 16) - str = sprintf_putchar(str, end, - 'X' | (flags & SPRINTF_FORMAT_LOWER)); - } else if (sign) { - str = sprintf_putchar(str, end, sign); - } - - if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY)) { - c = (flags & SPRINTF_FORMAT_ZERO_PAD) ? '0' : ' '; - - while (width-- > 0) { - str = sprintf_putchar(str, end, c); - } - } - - while (i < precision--) { - str = sprintf_putchar(str, end, '0'); - } - - while (i-- > 0) { - str = sprintf_putchar(str, end, tmp[i]); - } - - while (width-- > 0) { - str = sprintf_putchar(str, end, ' '); - } - - break; - case SPRINTF_SPECIFIER_CHAR: - c = (unsigned char)va_arg(ap, int); - - if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY)) - while (--width > 0) { - str = sprintf_putchar(str, end, ' '); - } - - str = sprintf_putchar(str, end, c); - - while (--width > 0) { - str = sprintf_putchar(str, end, ' '); - } - - break; - case SPRINTF_SPECIFIER_STR: - s = va_arg(ap, char *); - - if (s == NULL) { - s = "(null)"; - } - - len = 0; - - for (len = 0; s[len] != '\0'; len++) - if (len == precision) { - break; - } - - if (!(flags & SPRINTF_FORMAT_LEFT_JUSTIFY)) - while (len < width--) { - str = sprintf_putchar(str, end, ' '); - } - - for (i = 0; i < len; i++) { - str = sprintf_putchar(str, end, *s); - s++; - } - - while (len < width--) { - str = sprintf_putchar(str, end, ' '); - } - - break; - case SPRINTF_SPECIFIER_NRCHARS: - if (modifier == SPRINTF_MODIFIER_CHAR) { - signed char *ptr = va_arg(ap, signed char *); - *ptr = str - start; - } else if (modifier == SPRINTF_MODIFIER_SHORT) { - short *ptr = va_arg(ap, short *); - *ptr = str - start; - } else if (modifier == SPRINTF_MODIFIER_LONG) { - long *ptr = va_arg(ap, long *); - *ptr = str - start; - } else if (modifier == SPRINTF_MODIFIER_LONGLONG) { - long long *ptr = va_arg(ap, long long *); - *ptr = str - start; - } else if (modifier == SPRINTF_MODIFIER_SIZE) { - ssize_t *ptr = va_arg(ap, ssize_t *); - *ptr = str - start; - } else if (modifier == SPRINTF_MODIFIER_PTRDIFF) { - ptrdiff_t *ptr = va_arg(ap, ptrdiff_t *); - *ptr = str - start; - } else { - int *ptr = va_arg(ap, int *); - *ptr = str - start; - } - - break; - case SPRINTF_SPECIFIER_PERCENT: - case SPRINTF_SPECIFIER_INVALID: - str = sprintf_putchar(str, end, '%'); - break; - default: - break; - } - - if (specifier != SPRINTF_SPECIFIER_INVALID) { - format++; - } - } - - if (str < end) { - *str = '\0'; - } else if (end != NULL) { - *end = '\0'; - } - - return str - start; -} diff --git a/kern/sprintf.h b/kern/sprintf.h deleted file mode 100644 index 1cb51e7..0000000 --- a/kern/sprintf.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2010 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 3 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 . - * - * - * Formatted string functions. - * - * The functions provided by this module implement a subset of the C99 - * sprintf() like functions, mostly centered around character, string, and - * integer conversions. - * - * The supported specifiers are: d i o u x X c s p n % - * The supported length modifiers are: hh h l ll z t - */ - -#ifndef _KERN_SPRINTF_H -#define _KERN_SPRINTF_H - -#ifndef _STDIO_H -#error "do not use directly; include instead" -#endif /* _STDIO_H */ - -#include - -#include - -int sprintf(char *str, const char *format, ...) __format_printf(2, 3); -int vsprintf(char *str, const char *format, va_list ap) __format_printf(2, 0); - -int snprintf(char *str, size_t size, const char *format, ...) - __format_printf(3, 4); -int vsnprintf(char *str, size_t size, const char *format, va_list ap) - __format_printf(3, 0); - -#endif /* _KERN_SPRINTF_H */ -- cgit v1.2.3 From ec7c306bda9a7a4a8b656417e505041e97779a5c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 16:12:19 +0200 Subject: kern/log: new module --- Makefrag.am | 2 + arch/x86/machine/boot.c | 2 + kern/kernel.c | 2 + kern/log.c | 457 ++++++++++++++++++++++++++++++++++++++++++++++++ kern/log.h | 91 ++++++++++ 5 files changed, 554 insertions(+) create mode 100644 kern/log.c create mode 100644 kern/log.h diff --git a/Makefrag.am b/Makefrag.am index cab1e95..8718a94 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -44,6 +44,8 @@ x15_SOURCES += \ kern/llsync.c \ kern/llsync.h \ kern/llsync_i.h \ + kern/log.c \ + kern/log.h \ kern/log2.h \ kern/macros.h \ kern/mutex.c \ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 723976c..8812082 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -479,6 +480,7 @@ boot_save_data(void) void __init boot_main(void) { + log_setup(); arg_setup(boot_tmp_cmdline); sleepq_bootstrap(); turnstile_bootstrap(); diff --git a/kern/kernel.c b/kern/kernel.c index 1d61b0c..30dff08 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ kernel_main(void) sref_setup(); vm_page_info(); shell_start(); + log_start(); #ifdef X15_RUN_TEST_MODULE test_setup(); diff --git a/kern/log.c b/kern/log.c new file mode 100644 index 0000000..1320d3b --- /dev/null +++ b/kern/log.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 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 3 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 +#include + +#define LOG_BUFFER_SIZE 16384 + +#if !ISP2(LOG_BUFFER_SIZE) +#error "log buffer size must be a power-of-two" +#endif + +#define LOG_MSG_SIZE 128 + +#define LOG_MARKER 0x1 + +static struct thread *log_thread; + +static struct cbuf log_cbuf; +static char log_buffer[LOG_BUFFER_SIZE]; + +static unsigned long log_nr_overruns; + +/* + * Global lock. + * + * Interrupts must be disabled when holding this lock. + */ +static struct spinlock log_lock; + +/* + * A record starts with a marker byte and ends with a null terminating byte. + * + * There must be no null byte inside the header, and the buffer is expected + * to be a standard null-terminated string. + */ +struct log_record { + uint8_t mark; + uint8_t level; + char buffer[LOG_MSG_SIZE]; +}; + +static void +log_push(char c) +{ + if (cbuf_size(&log_cbuf) == cbuf_capacity(&log_cbuf)) { + log_nr_overruns++; + } + + cbuf_push(&log_cbuf, c); +} + +static const char * +log_level2str(unsigned int level) +{ + switch (level) { + case LOG_EMERG: + return "emerg"; + case LOG_ALERT: + return "alert"; + case LOG_CRIT: + return "crit"; + case LOG_ERR: + return "error"; + case LOG_WARNING: + return "warning"; + case LOG_NOTICE: + return "notice"; + case LOG_INFO: + return "info"; + case LOG_DEBUG: + return "debug"; + default: + return NULL; + } +} + +static char +log_level2char(unsigned int level) +{ + assert(level < LOG_NR_LEVELS); + return '0' + level; +} + +static uint8_t +log_char2level(char c) +{ + uint8_t level; + + level = c - '0'; + assert(level < LOG_NR_LEVELS); + return level; +} + +static void +log_record_init_produce(struct log_record *record, unsigned int level) +{ + record->mark = LOG_MARKER; + record->level = log_level2char(level); +} + +static void +log_record_consume(struct log_record *record, char c, size_t *sizep) +{ + char *ptr; + + assert(*sizep < sizeof(*record)); + + ptr = (char *)record; + ptr[*sizep] = c; + (*sizep)++; +} + +static int +log_record_init_consume(struct log_record *record) +{ + bool marker_found; + size_t size; + int error; + char c; + + marker_found = false; + size = 0; + + for (;;) { + error = cbuf_pop(&log_cbuf, &c); + + if (error) { + if (!marker_found) { + return ERROR_INVAL; + } + + break; + } + + if (!marker_found) { + if (c != LOG_MARKER) { + continue; + } + + marker_found = true; + log_record_consume(record, c, &size); + continue; + } else if (size == offsetof(struct log_record, level)) { + record->level = log_char2level(c); + size++; + continue; + } + + log_record_consume(record, c, &size); + + if (c == '\0') { + break; + } + } + + return 0; +} + +static void +log_record_print(const struct log_record *record) +{ + printf("%7s %s\n", log_level2str(record->level), record->buffer); +} + +static void +log_record_push(struct log_record *record) +{ + const char *ptr; + + ptr = (const char *)record; + + while (*ptr != '\0') { + log_push(*ptr); + ptr++; + } + + log_push('\0'); +} + +static void +log_run(void *arg) +{ + unsigned long flags, nr_overruns; + struct log_record record; + int error; + + (void)arg; + + nr_overruns = 0; + + for (;;) { + spinlock_lock_intr_save(&log_lock, &flags); + + while (cbuf_size(&log_cbuf) == 0) { + thread_sleep(&log_lock, &log_cbuf, "log_cbuf"); + } + + error = log_record_init_consume(&record); + + /* Drain the log buffer before reporting overruns */ + if (cbuf_size(&log_cbuf) == 0) { + nr_overruns = log_nr_overruns; + log_nr_overruns = 0; + } + + spinlock_unlock_intr_restore(&log_lock, flags); + + if (!error) { + log_record_print(&record); + } + + if (nr_overruns != 0) { + log_msg(LOG_ERR, "log: buffer overruns, %lu bytes dropped", + nr_overruns); + nr_overruns = 0; + } + } +} + +void __init +log_setup(void) +{ + cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); + spinlock_init(&log_lock); +} + +void __init +log_start(void) +{ + struct thread_attr attr; + int error; + + thread_attr_init(&attr, THREAD_KERNEL_PREFIX "log_run"); + thread_attr_set_detached(&attr); + error = thread_create(&log_thread, &attr, log_run, NULL); + + if (error) { + panic("log: unable to create thread"); + } +} + +int +log_msg(unsigned int level, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vmsg(level, format, ap); + va_end(ap); + + return ret; +} + +int +log_vmsg(unsigned int level, const char *format, va_list ap) +{ + struct log_record record; + unsigned long flags; + int nr_chars; + + log_record_init_produce(&record, level); + nr_chars = vsnprintf(record.buffer, sizeof(record.buffer), format, ap); + + if ((unsigned int)nr_chars >= sizeof(record.buffer)) { + log_msg(LOG_ERR, "log: message too large"); + goto out;; + } + + spinlock_lock_intr_save(&log_lock, &flags); + log_record_push(&record); + thread_wakeup(log_thread); + spinlock_unlock_intr_restore(&log_lock, flags); + +out: + return nr_chars; +} + +int +log_emerg(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vemerg(format, ap); + va_end(ap); + + return ret; +} + +int +log_alert(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_valert(format, ap); + va_end(ap); + + return ret; +} + +int +log_crit(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vcrit(format, ap); + va_end(ap); + + return ret; +} + +int +log_err(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_verr(format, ap); + va_end(ap); + + return ret; +} + +int +log_warning(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vwarning(format, ap); + va_end(ap); + + return ret; +} + +int +log_notice(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vnotice(format, ap); + va_end(ap); + + return ret; +} + +int +log_info(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vinfo(format, ap); + va_end(ap); + + return ret; +} + +int +log_debug(const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = log_vdebug(format, ap); + va_end(ap); + + return ret; +} + +int +log_vemerg(const char *format, va_list ap) +{ + return log_vmsg(LOG_EMERG, format, ap); +} + +int +log_valert(const char *format, va_list ap) +{ + return log_vmsg(LOG_ALERT, format, ap); +} + +int +log_vcrit(const char *format, va_list ap) +{ + return log_vmsg(LOG_CRIT, format, ap); +} + +int +log_verr(const char *format, va_list ap) +{ + return log_vmsg(LOG_ERR, format, ap); +} + +int +log_vwarning(const char *format, va_list ap) +{ + return log_vmsg(LOG_WARNING, format, ap); +} + +int +log_vnotice(const char *format, va_list ap) +{ + return log_vmsg(LOG_NOTICE, format, ap); +} + +int +log_vinfo(const char *format, va_list ap) +{ + return log_vmsg(LOG_INFO, format, ap); +} + +int +log_vdebug(const char *format, va_list ap) +{ + return log_vmsg(LOG_DEBUG, format, ap); +} diff --git a/kern/log.h b/kern/log.h new file mode 100644 index 0000000..a306ba3 --- /dev/null +++ b/kern/log.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * System logging. + */ + +#ifndef _KERN_LOG_H +#define _KERN_LOG_H + +#include + +enum { + LOG_EMERG, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG, + LOG_NR_LEVELS, +}; + +/* + * Initialize the log module. + */ +void log_setup(void); + +/* + * Start the log thread. + */ +void log_start(void); + +/* + * Generate a message and send it to the log thread. + * + * Except for level, the arguments and return value are similar to printf(). + * + * This function may safely be called in interrupt context. + */ +int log_msg(unsigned int level, const char *format, ...) + __attribute__((format(printf, 2, 3))); + +int log_vmsg(unsigned int level, const char *format, va_list ap) + __attribute__((format(printf, 2, 0))); + +/* + * Convenience wrappers. + */ + +int log_emerg(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_alert(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_crit(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_err(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_notice(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_info(const char *format, ...) __attribute__((format(printf, 1, 2))); +int log_debug(const char *format, ...) __attribute__((format(printf, 1, 2))); + +int log_vemerg(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_valert(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_vcrit(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_verr(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_vwarning(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_vnotice(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_vinfo(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); +int log_vdebug(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); + +#endif /* _KERN_LOG_H */ -- cgit v1.2.3 From 37f472fe53f16395b137f2f674ee3600a0ee0b8d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 16:46:21 +0200 Subject: x86/ioapic: include kern/macros.h --- arch/x86/machine/ioapic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 245c74c..3f53fce 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 562904ff8e2f5e40e4be76dc7c4d919a4bda05f8 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 17:23:24 +0200 Subject: kern/log: filter output based on log level --- kern/log.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kern/log.c b/kern/log.c index 1320d3b..90ae433 100644 --- a/kern/log.c +++ b/kern/log.c @@ -48,6 +48,8 @@ static char log_buffer[LOG_BUFFER_SIZE]; static unsigned long log_nr_overruns; +static unsigned int log_print_level; + /* * Global lock. * @@ -187,7 +189,15 @@ log_record_init_consume(struct log_record *record) static void log_record_print(const struct log_record *record) { - printf("%7s %s\n", log_level2str(record->level), record->buffer); + if (record->level > log_print_level) { + return; + } + + if (record->level <= LOG_WARNING) { + printf("%7s %s\n", log_level2str(record->level), record->buffer); + } else { + printf("%s\n", record->buffer); + } } static void @@ -250,6 +260,7 @@ log_setup(void) { cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); spinlock_init(&log_lock); + log_print_level = LOG_INFO; } void __init -- cgit v1.2.3 From b719ffa6583447ff933fbcf21274b376133cb992 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 17:24:04 +0200 Subject: Use log functions where appropriate --- arch/x86/machine/acpi.c | 24 +++++++++++------------ arch/x86/machine/atkbd.c | 10 +++++----- arch/x86/machine/biosmem.c | 49 ++++++++++++++++++---------------------------- arch/x86/machine/boot.c | 7 +++---- arch/x86/machine/cpu.c | 22 ++++++++++----------- arch/x86/machine/ioapic.c | 8 ++++---- arch/x86/machine/lapic.c | 10 +++++----- arch/x86/machine/pmap.c | 4 ++-- arch/x86/machine/strace.c | 15 +++++++------- arch/x86/machine/uart.c | 10 +++++----- kern/arg.c | 4 ++-- kern/console.c | 6 +++--- kern/intr.c | 6 +++--- kern/llsync.c | 4 ++-- kern/percpu.c | 14 ++++++------- kern/shell.c | 5 +++-- kern/sref.c | 4 ++-- kern/work.c | 10 +++++----- vm/vm_page.c | 30 ++++++++++++---------------- 19 files changed, 112 insertions(+), 130 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 6d5e06e..4da4d90 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -19,11 +19,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -193,8 +193,7 @@ acpi_register_table(struct acpi_sdth *table) for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { if (acpi_table_addrs[i].table != NULL) { - printf("acpi: warning: table %s ignored:" - " already registered\n", sig); + log_warning("acpi: table %s ignored: already registered", sig); return; } @@ -202,7 +201,7 @@ acpi_register_table(struct acpi_sdth *table) return; } - printf("acpi: warning: table '%s' ignored: unknown table\n", sig); + log_warning("acpi: table '%s' ignored: unknown table", sig); } static struct acpi_sdth * __init @@ -225,8 +224,7 @@ acpi_check_tables(void) for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) if (acpi_table_addrs[i].table == NULL) { - printf("acpi: error: table %s missing\n", - acpi_table_addrs[i].sig); + log_err("acpi: table %s missing", acpi_table_addrs[i].sig); return -1; } @@ -359,7 +357,7 @@ acpi_find_rsdp(struct acpi_rsdp *rsdp) return 0; } - printf("acpi: unable to find root system description pointer\n"); + log_debug("acpi: unable to find root system description pointer"); return -1; } @@ -370,8 +368,8 @@ acpi_info(void) rsdt = acpi_lookup_table("RSDT"); assert(rsdt != NULL); - printf("acpi: revision: %u, oem: %.*s\n", rsdt->revision, - (int)sizeof(rsdt->oem_id), rsdt->oem_id); + log_debug("acpi: revision: %u, oem: %.*s", rsdt->revision, + (int)sizeof(rsdt->oem_id), rsdt->oem_id); } static struct acpi_sdth * __init @@ -409,7 +407,7 @@ acpi_copy_table(uint32_t addr) char sig[ACPI_SIG_SIZE]; acpi_table_sig(table, sig); - printf("acpi: table %s: invalid checksum\n", sig); + log_err("acpi: table %s: invalid checksum", sig); copy = NULL; goto out; } @@ -512,7 +510,7 @@ acpi_load_iso(const struct acpi_madt_entry_iso *iso) bool active_high, edge_triggered; if (iso->bus != 0) { - printf("acpi: error: invalid interrupt source override bus\n"); + log_err("acpi: invalid interrupt source override bus"); return; } @@ -525,7 +523,7 @@ acpi_load_iso(const struct acpi_madt_entry_iso *iso) active_high = false; break; default: - printf("acpi: error: invalid polarity\n"); + log_err("acpi: invalid polarity"); return; } @@ -538,7 +536,7 @@ acpi_load_iso(const struct acpi_madt_entry_iso *iso) edge_triggered = false; break; default: - printf("acpi: error: invalid trigger mode\n"); + log_err("acpi: invalid trigger mode"); return; } diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index 1ed4c86..da792bd 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -24,11 +24,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -489,13 +489,13 @@ atkbd_read_status(bool check_out) * of hardware. */ if (status == 0xff) { - printf("atkbd: no keyboard controller\n"); + log_info("atkbd: no keyboard controller"); return ERROR_NODEV; } else if (status & ATKBD_STATUS_PARITY_ERROR) { - printf("atkbd: parity error\n"); + log_err("atkbd: parity error"); return ERROR_IO; } else if (status & ATKBD_STATUS_TIMEOUT_ERROR) { - printf("atkbd: timeout error\n"); + log_err("atkbd: timeout error"); return ERROR_TIMEDOUT; } @@ -849,7 +849,7 @@ atkbd_setup(void) error = intr_register(ATKBD_INTR1, atkbd_intr, NULL); if (error) { - printf("atkbd: error: unable to register interrupt handler\n"); + log_err("atkbd: unable to register interrupt handler"); return; } diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index bfa22af..ec090d0 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -19,10 +19,10 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -34,8 +34,6 @@ #include #include -#define DEBUG 0 - #define BIOSMEM_MAX_BOOT_DATA 64 /* @@ -228,11 +226,9 @@ biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end) 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 */ + log_debug("biosmem: unregister boot data: %llx:%llx", + (unsigned long long)biosmem_boot_data_array[i].start, + (unsigned long long)biosmem_boot_data_array[i].end); biosmem_nr_boot_data--; @@ -763,8 +759,6 @@ biosmem_directmap_end(void) } } -#if DEBUG - static const char * __init biosmem_type_desc(unsigned int type) { @@ -789,24 +783,20 @@ biosmem_map_show(void) { const struct biosmem_map_entry *entry, *end; - printf("biosmem: physical memory map:\n"); + log_debug("biosmem: physical memory map:"); for (entry = biosmem_map, end = entry + biosmem_map_size; entry < end; entry++) - printf("biosmem: %018llx:%018llx, %s\n", entry->base_addr, - entry->base_addr + entry->length, - biosmem_type_desc(entry->type)); + log_debug("biosmem: %018llx:%018llx, %s", entry->base_addr, + entry->base_addr + entry->length, + biosmem_type_desc(entry->type)); - printf("biosmem: heap: %llx:%llx\n", - (unsigned long long)biosmem_heap_start, - (unsigned long long)biosmem_heap_end); + log_debug("biosmem: heap: %llx:%llx", + (unsigned long long)biosmem_heap_start, + (unsigned long long)biosmem_heap_end); } -#else /* DEBUG */ -#define biosmem_map_show() -#endif /* DEBUG */ - static void __init biosmem_load_zone(struct biosmem_zone *zone, uint64_t max_phys_end) { @@ -819,13 +809,14 @@ biosmem_load_zone(struct biosmem_zone *zone, uint64_t max_phys_end) if (phys_end > max_phys_end) { if (max_phys_end <= phys_start) { - printf("biosmem: warning: zone %s physically unreachable, " - "not loaded\n", vm_page_zone_name(zone_index)); + log_warning("biosmem: zone %s physically unreachable, " + "not loaded", vm_page_zone_name(zone_index)); return; } - printf("biosmem: warning: zone %s truncated to %#llx\n", - vm_page_zone_name(zone_index), (unsigned long long)max_phys_end); + log_warning("biosmem: warning: zone %s truncated to %#llx", + vm_page_zone_name(zone_index), + (unsigned long long)max_phys_end); phys_end = max_phys_end; } @@ -901,11 +892,9 @@ 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 + log_debug("biosmem: release to vm_page: %llx:%llx (%lluk)", + (unsigned long long)start, (unsigned long long)end, + (unsigned long long)((end - start) >> 10)); while (start < end) { page = vm_page_lookup(start); diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 8812082..355b7df 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include @@ -350,11 +349,11 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) static void __init boot_show_version(void) { - printf(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION + log_info(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION #ifdef X15_X86_PAE - " PAE" + " PAE" #endif /* X15_X86_PAE */ - "\n"); + ); } static void * __init diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 846b21e..4981ff6 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -18,10 +18,10 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -591,22 +591,22 @@ cpu_check(const struct cpu *cpu) void cpu_info(const struct cpu *cpu) { - printf("cpu%u: %s, type %u, family %u, model %u, stepping %u\n", - cpu->id, cpu->vendor_id, cpu->type, cpu->family, cpu->model, - cpu->stepping); + log_info("cpu%u: %s, type %u, family %u, model %u, stepping %u", + cpu->id, cpu->vendor_id, cpu->type, cpu->family, cpu->model, + cpu->stepping); if (strlen(cpu->model_name) > 0) { - printf("cpu%u: %s\n", cpu->id, cpu->model_name); + log_info("cpu%u: %s", cpu->id, cpu->model_name); } if ((cpu->phys_addr_width != 0) && (cpu->virt_addr_width != 0)) { - printf("cpu%u: address widths: physical: %hu, virtual: %hu\n", - cpu->id, cpu->phys_addr_width, cpu->virt_addr_width); + log_info("cpu%u: address widths: physical: %hu, virtual: %hu", + cpu->id, cpu->phys_addr_width, cpu->virt_addr_width); } - printf("cpu%u: frequency: %llu.%02llu MHz\n", cpu->id, - (unsigned long long)cpu_freq / 1000000, - (unsigned long long)cpu_freq % 1000000); + log_info("cpu%u: frequency: %llu.%02llu MHz", cpu->id, + (unsigned long long)cpu_freq / 1000000, + (unsigned long long)cpu_freq % 1000000); } void __init @@ -654,7 +654,7 @@ cpu_mp_probe(void) pic_setup(); } - printf("cpu: %u processor(s) configured\n", cpu_count()); + log_info("cpu: %u processor(s) configured", cpu_count()); } void __init diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 245c74c..cd3d2d3 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -18,12 +18,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -101,7 +101,7 @@ ioapic_alloc_iso(void) struct ioapic_iso *iso; if (ioapic_nr_isos >= ARRAY_SIZE(ioapic_isos)) { - printf("ioapic: error: too many interrupt overrides\n"); + log_err("ioapic: too many interrupt overrides"); return NULL; } @@ -201,8 +201,8 @@ ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int gsi_base) trap_register(TRAP_INTR_FIRST + i, ioapic_intr); } - printf("ioapic%u: version:%#x gsis:%u-%u\n", ioapic->id, - ioapic->version, ioapic->first_gsi, ioapic->last_gsi); + log_info("ioapic%u: version:%#x gsis:%u-%u", ioapic->id, + ioapic->version, ioapic->first_gsi, ioapic->last_gsi); ioapic_nr_devs++; return ioapic; diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index dad3bd7..c20eec7 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -19,9 +19,9 @@ #include #include #include -#include #include +#include #include #include #include @@ -210,8 +210,8 @@ lapic_compute_freq(void) cpu_delay(LAPIC_TIMER_CAL_DELAY); c2 = lapic_read(&lapic_map->timer_ccr); lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY); - printf("lapic: bus frequency: %u.%02u MHz\n", lapic_bus_freq / 1000000, - lapic_bus_freq % 1000000); + log_info("lapic: bus frequency: %u.%02u MHz", lapic_bus_freq / 1000000, + lapic_bus_freq % 1000000); lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); lapic_write(&lapic_map->svr, 0); } @@ -344,7 +344,7 @@ lapic_error_intr(struct trap_frame *frame) (void)frame; esr = lapic_read(&lapic_map->esr); - printf("lapic: error on cpu%u: esr:%08x\n", cpu_id(), esr); + log_err("lapic: error on cpu%u: esr:%08x", cpu_id(), esr); lapic_write(&lapic_map->esr, 0); lapic_eoi(); } @@ -353,7 +353,7 @@ void lapic_spurious_intr(struct trap_frame *frame) { (void)frame; - printf("lapic: warning: spurious interrupt\n"); + log_warning("lapic: spurious interrupt"); /* No EOI for this interrupt */ } diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 1aab9cc..0520c13 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1162,7 +1162,7 @@ pmap_enter_local(struct pmap *pmap, uintptr_t va, phys_addr_t pa, page = vm_page_alloc(0, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_PMAP); if (page == NULL) { - printf("pmap: warning: page table page allocation failure\n"); + log_warning("pmap: page table page allocation failure"); return ERROR_NOMEM; } diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index 8aea4b0..7ff47ef 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -133,14 +134,14 @@ strace_copy_section(const struct elf_shdr *shdr) src = vm_kmem_map_pa(shdr->addr, shdr->size, &map_addr, &map_size); if (src == NULL) { - printf("strace: unable to map section\n"); + log_err("strace: unable to map section"); goto error_map; } copy = kmem_alloc(shdr->size); if (copy == NULL) { - printf("strace: unable to allocate section copy\n"); + log_err("strace: unable to allocate section copy"); goto error_copy; } @@ -191,12 +192,12 @@ strace_setup(const struct multiboot_raw_info *mbi) table = vm_kmem_map_pa(mbi->shdr_addr, size, &map_addr, &map_size); if (table == NULL) { - printf("strace: unable to map section headers table\n"); + log_err("strace: unable to map section headers table"); goto no_syms; } if (mbi->shdr_strndx >= mbi->shdr_num) { - printf("strace: invalid section names index\n"); + log_err("strace: invalid section names index"); goto error_shstrndx; } @@ -205,21 +206,21 @@ strace_setup(const struct multiboot_raw_info *mbi) &shstrtab_map_addr, &shstrtab_map_size); if (shstrtab == NULL) { - printf("strace: unable to map section names\n"); + log_err("strace: unable to map section names"); goto error_shstrtab; } symtab_hdr = strace_lookup_section(mbi, table, shstrtab, ".symtab"); if (symtab_hdr == NULL) { - printf("strace: unable to find symbol table\n"); + log_err("strace: unable to find symbol table"); goto error_symtab_lookup; } strtab_hdr = strace_lookup_section(mbi, table, shstrtab, ".strtab"); if (strtab_hdr == NULL) { - printf("strace: unable to find symbol string table\n"); + log_err("strace: unable to find symbol string table"); goto error_strtab_lookup; } diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index a4a01cb..22d3948 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -20,12 +20,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -180,8 +180,8 @@ uart_enable_intr(struct uart *uart) error = intr_register(uart->intr, uart_intr, uart); if (error) { - printf("uart%zu: error: unable to register interrupt %u\n", - uart_get_id(uart), uart->intr); + log_err("uart%zu: unable to register interrupt %u", + uart_get_id(uart), uart->intr); return; } @@ -312,8 +312,8 @@ uart_info(void) uart = uart_get_dev(i); if (uart->port != 0) { - printf("uart%zu: port:%#x irq:%u\n", i, (unsigned int)uart->port, - (unsigned int)uart->intr); + log_info("uart%zu: port:%#x irq:%u", i, (unsigned int)uart->port, + (unsigned int)uart->intr); } } } diff --git a/kern/arg.c b/kern/arg.c index 9c35651..ed684ce 100644 --- a/kern/arg.c +++ b/kern/arg.c @@ -18,11 +18,11 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -72,7 +72,7 @@ arg_info(void) } cmdline[i] = '\0'; - printf("arg: %s\n", cmdline); + log_info("arg: %s", cmdline); } static const char * __init diff --git a/kern/console.c b/kern/console.c index fef4f65..bee400e 100644 --- a/kern/console.c +++ b/kern/console.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -128,10 +128,10 @@ console_register(struct console *console) console_dev = console; } - printf("console: %s registered\n", console->name); + log_info("console: %s registered", console->name); if (console == console_dev) { - printf("console: %s selected as active console\n", console->name); + log_info("console: %s selected as active console", console->name); } } diff --git a/kern/intr.c b/kern/intr.c index fe69e3e..ca733e2 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -24,13 +24,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -388,7 +388,7 @@ intr_unregister(unsigned int intr, intr_handler_fn_t fn) handler = intr_entry_remove(intr_get_entry(intr), fn); if (handler == NULL) { - printf("intr: warning: attempting to unregister unknown handler\n"); + log_warning("intr: attempting to unregister unknown handler"); return; } @@ -410,7 +410,7 @@ intr_handle(unsigned int intr) spinlock_lock(&entry->lock); if (intr_entry_empty(entry)) { - printf("intr: spurious interrupt %u\n", intr); + log_warning("intr: spurious interrupt %u", intr); goto out; } diff --git a/kern/llsync.c b/kern/llsync.c index 1c5086f..4704e80 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -35,12 +35,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -122,7 +122,7 @@ llsync_process_global_checkpoint(void) /* TODO Handle hysteresis */ if (!llsync_data.no_warning && (nr_works >= LLSYNC_NR_PENDING_WORKS_WARN)) { llsync_data.no_warning = 1; - printf("llsync: warning: large number of pending works\n"); + log_warning("llsync: large number of pending works\n"); } if (llsync_data.nr_registered_cpus == 0) { diff --git a/kern/percpu.c b/kern/percpu.c index a1f7df1..805825a 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -18,11 +18,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -50,8 +50,8 @@ percpu_setup(void) unsigned int order; percpu_area_size = &_percpu_end - &_percpu; - printf("percpu: max_cpus: %u, section size: %zuk\n", X15_MAX_CPUS, - percpu_area_size >> 10); + log_info("percpu: max_cpus: %u, section size: %zuk", X15_MAX_CPUS, + percpu_area_size >> 10); assert(vm_page_aligned(percpu_area_size)); if (percpu_area_size == 0) { @@ -77,8 +77,8 @@ percpu_add(unsigned int cpu) if (cpu >= ARRAY_SIZE(percpu_areas)) { if (!percpu_skip_warning) { - printf("percpu: ignoring processor beyond id %zu\n", - ARRAY_SIZE(percpu_areas) - 1); + log_warning("percpu: ignoring processor beyond id %zu", + ARRAY_SIZE(percpu_areas) - 1); percpu_skip_warning = 1; } @@ -86,7 +86,7 @@ percpu_add(unsigned int cpu) } if (percpu_areas[cpu] != NULL) { - printf("percpu: error: id %u ignored, already registered\n", cpu); + log_err("percpu: id %u ignored, already registered", cpu); return ERROR_INVAL; } @@ -98,7 +98,7 @@ percpu_add(unsigned int cpu) page = vm_page_alloc(order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL); if (page == NULL) { - printf("percpu: error: unable to allocate percpu area\n"); + log_err("percpu: unable to allocate percpu area"); return ERROR_NOMEM; } diff --git a/kern/shell.c b/kern/shell.c index 38f2991..c526b31 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -20,8 +20,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -445,7 +446,7 @@ shell_cmd_add(struct shell_cmd *cmd) for (;;) { if (strcmp(cmd->name, tmp->name) == 0) { - printf("shell: %s: shell command name collision", cmd->name); + log_err("shell: %s: shell command name collision", cmd->name); return ERROR_EXIST; } diff --git a/kern/sref.c b/kern/sref.c index 9c2d776..5aad8da 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -44,12 +44,12 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -503,7 +503,7 @@ sref_end_epoch(struct sref_queue *queue) if (!sref_data.no_warning && (sref_review_queue_size() >= SREF_NR_COUNTERS_WARN)) { sref_data.no_warning = 1; - printf("sref: warning: large number of counters in review queue\n"); + log_warning("sref: large number of counters in review queue"); } if (sref_data.nr_registered_cpus == 1) { diff --git a/kern/work.c b/kern/work.c index 5c33eb7..284b1e2 100644 --- a/kern/work.c +++ b/kern/work.c @@ -17,13 +17,13 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -374,7 +374,7 @@ work_process(void *arg) if (error) { work_pool_free_id(pool, id); - printf("work: warning: unable to create worker thread\n"); + log_warning("work: unable to create worker thread"); } } } @@ -490,9 +490,9 @@ work_setup(void) work_pool_init(&work_pool_highprio, WORK_INVALID_CPU, WORK_PF_GLOBAL | WORK_PF_HIGHPRIO); - printf("work: threads per pool (per-cpu/global): %u/%u, spare: %u\n", - percpu_var(work_pool_cpu_main.max_threads, 0), - work_pool_main.max_threads, WORK_THREADS_SPARE); + log_info("work: threads per pool (per-cpu/global): %u/%u, spare: %u", + percpu_var(work_pool_cpu_main.max_threads, 0), + work_pool_main.max_threads, WORK_THREADS_SPARE); } void diff --git a/vm/vm_page.c b/vm/vm_page.c index edf213e..a6c8c6b 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -33,11 +33,11 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -48,8 +48,6 @@ #include #include -#define DEBUG 0 - /* * Number of free block lists per zone. */ @@ -510,11 +508,9 @@ vm_page_load(unsigned int zone_index, phys_addr_t start, phys_addr_t end) zone->end = end; zone->heap_present = false; -#if DEBUG - printf("vm_page: load: %s: %llx:%llx\n", - vm_page_zone_name(zone_index), - (unsigned long long)start, (unsigned long long)end); -#endif + log_debug("vm_page: load: %s: %llx:%llx\n", + vm_page_zone_name(zone_index), + (unsigned long long)start, (unsigned long long)end); vm_page_zones_size++; } @@ -537,11 +533,9 @@ vm_page_load_heap(unsigned int zone_index, phys_addr_t start, phys_addr_t end) zone->avail_end = end; zone->heap_present = true; -#if DEBUG - printf("vm_page: heap: %s: %llx:%llx\n", - vm_page_zone_name(zone_index), - (unsigned long long)start, (unsigned long long)end); -#endif + log_debug("vm_page: heap: %s: %llx:%llx", + vm_page_zone_name(zone_index), + (unsigned long long)start, (unsigned long long)end); } int @@ -663,8 +657,8 @@ vm_page_setup(void) } table_size = vm_page_round(nr_pages * sizeof(struct vm_page)); - printf("vm_page: page table size: %zu entries (%zuk)\n", nr_pages, - table_size >> 10); + log_info("vm_page: page table size: %zu entries (%zuk)", nr_pages, + table_size >> 10); table = vm_page_bootalloc(table_size); va = (uintptr_t)table; @@ -781,8 +775,8 @@ vm_page_info(void) for (i = 0; i < vm_page_zones_size; i++) { zone = &vm_page_zones[i]; pages = (unsigned long)(zone->pages_end - zone->pages); - printf("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)\n", - vm_page_zone_name(i), pages, pages >> (20 - PAGE_SHIFT), - zone->nr_free_pages, zone->nr_free_pages >> (20 - PAGE_SHIFT)); + log_info("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)", + vm_page_zone_name(i), pages, pages >> (20 - PAGE_SHIFT), + zone->nr_free_pages, zone->nr_free_pages >> (20 - PAGE_SHIFT)); } } -- cgit v1.2.3 From be23ca8c9643966e4c685b2d36ef5188e7be6e3e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 18:10:47 +0200 Subject: kern/shell: new SHELL_REGISTER_CMDS macro --- kern/shell.h | 14 ++++++++++++++ kern/task.c | 8 +------- kern/thread.c | 8 ++------ 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/kern/shell.h b/kern/shell.h index a33abbe..10bc8a3 100644 --- a/kern/shell.h +++ b/kern/shell.h @@ -21,6 +21,20 @@ #ifndef _KERN_SHELL_H #define _KERN_SHELL_H +#include +#include + +#define SHELL_REGISTER_CMDS(cmds) \ +MACRO_BEGIN \ + size_t ___i; \ + int ___error; \ + \ + for (___i = 0; ___i < ARRAY_SIZE(cmds); ___i++) { \ + ___error = shell_cmd_register(&(cmds)[___i]); \ + error_check(___error, __func__); \ + } \ +MACRO_END + typedef void (*shell_fn_t)(int argc, char *argv[]); struct shell_cmd { diff --git a/kern/task.c b/kern/task.c index 66b43bf..0b5217e 100644 --- a/kern/task.c +++ b/kern/task.c @@ -99,19 +99,13 @@ static struct shell_cmd task_shell_cmds[] = { void __init task_setup(void) { - unsigned int i; - int error; - kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0); list_init(&task_list); spinlock_init(&task_list_lock); task_init(kernel_task, "x15", kernel_map); list_insert_head(&task_list, &kernel_task->node); - for (i = 0; i < ARRAY_SIZE(task_shell_cmds); i++) { - error = shell_cmd_register(&task_shell_cmds[i]); - error_check(error, __func__); - } + SHELL_REGISTER_CMDS(task_shell_cmds); } int diff --git a/kern/thread.c b/kern/thread.c index 25d8177..a753eb5 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2282,8 +2282,7 @@ static struct shell_cmd thread_shell_cmds[] = { void __init thread_setup(void) { - unsigned int i; - int cpu, error; + int cpu; for (cpu = 1; (unsigned int)cpu < cpu_count(); cpu++) { thread_bootstrap_common(cpu); @@ -2302,10 +2301,7 @@ thread_setup(void) thread_setup_runq(percpu_ptr(thread_runq, cpu)); } - for (i = 0; i < ARRAY_SIZE(thread_shell_cmds); i++) { - error = shell_cmd_register(&thread_shell_cmds[i]); - error_check(error, __func__); - } + SHELL_REGISTER_CMDS(thread_shell_cmds); } int -- cgit v1.2.3 From 164ccc1eeef9b335efa48ff3985a5c1c35bdecad Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 18:41:04 +0200 Subject: kern/log: new log_dump shell command --- kern/log.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/kern/log.c b/kern/log.c index 90ae433..121bd74 100644 --- a/kern/log.c +++ b/kern/log.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -141,7 +142,7 @@ log_record_consume(struct log_record *record, char c, size_t *sizep) } static int -log_record_init_consume(struct log_record *record) +log_record_init_consume(struct log_record *record, unsigned long *indexp) { bool marker_found; size_t size; @@ -152,9 +153,7 @@ log_record_init_consume(struct log_record *record) size = 0; for (;;) { - error = cbuf_pop(&log_cbuf, &c); - - if (error) { + if (*indexp == cbuf_end(&log_cbuf)) { if (!marker_found) { return ERROR_INVAL; } @@ -162,6 +161,15 @@ log_record_init_consume(struct log_record *record) break; } + error = cbuf_read(&log_cbuf, *indexp, &c); + + if (error) { + *indexp = cbuf_start(&log_cbuf); + continue; + } + + (*indexp)++; + if (!marker_found) { if (c != LOG_MARKER) { continue; @@ -187,9 +195,9 @@ log_record_init_consume(struct log_record *record) } static void -log_record_print(const struct log_record *record) +log_record_print(const struct log_record *record, unsigned int level) { - if (record->level > log_print_level) { + if (record->level > level) { return; } @@ -218,7 +226,7 @@ log_record_push(struct log_record *record) static void log_run(void *arg) { - unsigned long flags, nr_overruns; + unsigned long flags, index, nr_overruns; struct log_record record; int error; @@ -226,14 +234,16 @@ log_run(void *arg) nr_overruns = 0; - for (;;) { - spinlock_lock_intr_save(&log_lock, &flags); + spinlock_lock_intr_save(&log_lock, &flags); + + index = cbuf_start(&log_cbuf); + for (;;) { while (cbuf_size(&log_cbuf) == 0) { thread_sleep(&log_lock, &log_cbuf, "log_cbuf"); } - error = log_record_init_consume(&record); + error = log_record_init_consume(&record, &index); /* Drain the log buffer before reporting overruns */ if (cbuf_size(&log_cbuf) == 0) { @@ -244,7 +254,7 @@ log_run(void *arg) spinlock_unlock_intr_restore(&log_lock, flags); if (!error) { - log_record_print(&record); + log_record_print(&record, log_print_level); } if (nr_overruns != 0) { @@ -252,7 +262,57 @@ log_run(void *arg) nr_overruns); nr_overruns = 0; } + + spinlock_lock_intr_save(&log_lock, &flags); + } +} + +static void +log_dump(unsigned int level) +{ + unsigned long index, flags; + struct log_record record; + int error; + + spinlock_lock_intr_save(&log_lock, &flags); + + index = cbuf_start(&log_cbuf); + + for (;;) { + error = log_record_init_consume(&record, &index); + + if (error) { + break; + } + + spinlock_unlock_intr_restore(&log_lock, flags); + + log_record_print(&record, level); + + spinlock_lock_intr_save(&log_lock, &flags); + } + + spinlock_unlock_intr_restore(&log_lock, flags); +} + +static void +log_shell_dump(int argc, char **argv) +{ + unsigned int level; + int ret; + + if (argc != 2) { + level = log_print_level; + } else { + ret = sscanf(argv[1], "%u", &level); + + if ((ret != 1) || (level >= LOG_NR_LEVELS)) { + printf("log: dump: invalid arguments\n"); + return; + } } + + log_dump(level); } void __init @@ -263,6 +323,12 @@ log_setup(void) log_print_level = LOG_INFO; } +static struct shell_cmd log_shell_cmds[] = { + SHELL_CMD_INITIALIZER("log_dump", log_shell_dump, + "logdump []", + "dump the log buffer"), +}; + void __init log_start(void) { @@ -276,6 +342,8 @@ log_start(void) if (error) { panic("log: unable to create thread"); } + + SHELL_REGISTER_CMDS(log_shell_cmds); } int -- cgit v1.2.3 From 4904e60618fb9335d7e3933dfc42974a798e76ea Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 18:47:46 +0200 Subject: x86/biosmem: fix log argument type --- arch/x86/machine/biosmem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index ec090d0..8be00b1 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -788,8 +788,9 @@ biosmem_map_show(void) for (entry = biosmem_map, end = entry + biosmem_map_size; entry < end; entry++) - log_debug("biosmem: %018llx:%018llx, %s", entry->base_addr, - entry->base_addr + entry->length, + log_debug("biosmem: %018llx:%018llx, %s", + (unsigned long long)entry->base_addr, + (unsigned long long)(entry->base_addr + entry->length), biosmem_type_desc(entry->type)); log_debug("biosmem: heap: %llx:%llx", -- cgit v1.2.3 From 0144ca4912123db2788f98b36c4531fc86aeb078 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 19:04:03 +0200 Subject: kern/log: fix buffer consumption from log thread --- kern/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/log.c b/kern/log.c index 121bd74..9c2c5fc 100644 --- a/kern/log.c +++ b/kern/log.c @@ -239,7 +239,7 @@ log_run(void *arg) index = cbuf_start(&log_cbuf); for (;;) { - while (cbuf_size(&log_cbuf) == 0) { + while (index == cbuf_end(&log_cbuf)) { thread_sleep(&log_lock, &log_cbuf, "log_cbuf"); } -- cgit v1.2.3 From 9dec3deb955f3e563987b94766f4a9465a81d99d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 19:17:42 +0200 Subject: kern/task: minor shell improvements --- kern/task.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kern/task.c b/kern/task.c index 0b5217e..d9c9f11 100644 --- a/kern/task.c +++ b/kern/task.c @@ -93,7 +93,8 @@ error: static struct shell_cmd task_shell_cmds[] = { SHELL_CMD_INITIALIZER("task_info", task_shell_info, - "task_info", "print all tasks and threads"), + "task_info []", + "print tasks and threads"), }; void __init @@ -213,7 +214,7 @@ task_info(struct task *task) spinlock_lock(&task_list_lock); list_for_each_entry(&task_list, task, node) { - printf("task: %s\n", task->name); + task_info(task); } spinlock_unlock(&task_list_lock); -- cgit v1.2.3 From 52632a67f662dc0930984755ebb20d1b9d0195fc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 19:34:56 +0200 Subject: kern/log: remove extra semi-column --- kern/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/log.c b/kern/log.c index 9c2c5fc..9933ecc 100644 --- a/kern/log.c +++ b/kern/log.c @@ -371,7 +371,7 @@ log_vmsg(unsigned int level, const char *format, va_list ap) if ((unsigned int)nr_chars >= sizeof(record.buffer)) { log_msg(LOG_ERR, "log: message too large"); - goto out;; + goto out; } spinlock_lock_intr_save(&log_lock, &flags); -- cgit v1.2.3 From 7cc26ce0a192814174b93dbb387a4ff69e341138 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 19:37:37 +0200 Subject: x86/boot: log version and arguments early --- arch/x86/machine/boot.c | 6 +++--- kern/arg.c | 2 +- kern/arg.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 355b7df..5f27d90 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -347,7 +347,7 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) } static void __init -boot_show_version(void) +boot_log_version(void) { log_info(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION #ifdef X15_X86_PAE @@ -488,13 +488,13 @@ boot_main(void) trap_setup(); cpu_setup(); thread_bootstrap(); + boot_log_version(); + arg_log_info(); console_setup(); atcons_bootstrap(); uart_bootstrap(); printf_setup(); - boot_show_version(); shell_setup(); - arg_info(); uart_info(); pmap_bootstrap(); sref_bootstrap(); diff --git a/kern/arg.c b/kern/arg.c index ed684ce..62ace8b 100644 --- a/kern/arg.c +++ b/kern/arg.c @@ -62,7 +62,7 @@ arg_setup(const char *cmdline) } void __init -arg_info(void) +arg_log_info(void) { char cmdline[sizeof(arg_cmdline)]; size_t i; diff --git a/kern/arg.h b/kern/arg.h index 937f596..3a8d20b 100644 --- a/kern/arg.h +++ b/kern/arg.h @@ -35,9 +35,9 @@ void arg_setup(const char *cmdline); /* - * Display command line information. + * Log command line information. */ -void arg_info(void); +void arg_log_info(void); /* * Return true if an argument with the given name is present in the -- cgit v1.2.3 From 3b02fcfb2d3df5f849b2544f3064d2ca346d319e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 10 Jun 2017 20:42:50 +0200 Subject: x86/strace: tweak stack tracing output --- arch/x86/machine/strace.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index 7ff47ef..6c28cf1 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -75,9 +75,9 @@ strace_show_one(unsigned int index, unsigned long ip) name = strace_lookup(ip, &offset, &size); if (name == NULL) { - printf("strace: #%u [" STRACE_ADDR_FORMAT "]\n", index, ip); + printf("#%u [" STRACE_ADDR_FORMAT "]\n", index, ip); } else { - printf("strace: #%u [" STRACE_ADDR_FORMAT "] %s+%#lx/%#lx\n", + printf("#%u [" STRACE_ADDR_FORMAT "] %s+%#lx/%#lx\n", index, ip, name, (unsigned long)offset, (unsigned long)size); } } @@ -90,7 +90,6 @@ strace_show(unsigned long ip, unsigned long bp) unsigned int i; int error; - printf("strace: stack trace:\n"); strace_show_one(0, ip); i = 1; @@ -119,8 +118,6 @@ strace_show(unsigned long ip, unsigned long bp) i++; frame = frame[0]; } - - printf("strace: end of trace\n"); } static void * __init -- cgit v1.2.3 From 7f72ab7149a63cdb012f2be9be170cbc7794dddf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 11 Jun 2017 00:48:00 +0200 Subject: kern/fmt: fix 0 and octal integer parsing --- kern/fmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kern/fmt.c b/kern/fmt.c index 06284de..1c635e1 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -1110,11 +1110,17 @@ fmt_sscanf_state_produce_int(struct fmt_sscanf_state *state) c = fmt_sscanf_state_consume_string(state); } else { fmt_sscanf_state_restore_string(state); + c = '0'; } } else { if (state->base == 0) { state->base = 8; } + + if (state->base != 8) { + fmt_sscanf_state_restore_string(state); + c = '0'; + } } } -- cgit v1.2.3 From ca6d28b7616945ddcc1e29a61176e1d54a0300a2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 11 Jun 2017 00:30:21 +0200 Subject: vm/vm_page: fix debug message --- vm/vm_page.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/vm_page.c b/vm/vm_page.c index a6c8c6b..dd17573 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -508,7 +508,7 @@ vm_page_load(unsigned int zone_index, phys_addr_t start, phys_addr_t end) zone->end = end; zone->heap_present = false; - log_debug("vm_page: load: %s: %llx:%llx\n", + log_debug("vm_page: load: %s: %llx:%llx", vm_page_zone_name(zone_index), (unsigned long long)start, (unsigned long long)end); -- cgit v1.2.3 From cd56e2cf82938061b101cf752ce0db195c4009ed Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 11 Jun 2017 01:41:32 +0200 Subject: kern/log: improve shell command online help --- kern/log.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/kern/log.c b/kern/log.c index 9933ecc..b802909 100644 --- a/kern/log.c +++ b/kern/log.c @@ -324,9 +324,19 @@ log_setup(void) } static struct shell_cmd log_shell_cmds[] = { - SHELL_CMD_INITIALIZER("log_dump", log_shell_dump, - "logdump []", - "dump the log buffer"), + SHELL_CMD_INITIALIZER2("log_dump", log_shell_dump, + "log_dump []", + "dump the log buffer", + "Only records of level less than or equal to the given level" + " are printed. Level may be one of :\n" + " 0: emergency\n" + " 1: alert\n" + " 2: critical\n" + " 3: error\n" + " 4: warning\n" + " 5: notice\n" + " 6: info\n" + " 7: debug"), }; void __init -- cgit v1.2.3 From b135fef80cf8f9addf5725881a2fe81cda404dfe Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 11 Jun 2017 11:27:32 +0200 Subject: Make the diagnostics shell optional --- Makefrag.am | 8 ++++++-- configure.ac | 10 ++++++++++ kern/log.c | 20 ++++++++++++-------- kern/shell.h | 8 ++++++++ kern/task.c | 4 ++++ kern/thread.c | 4 ++++ 6 files changed, 44 insertions(+), 10 deletions(-) diff --git a/Makefrag.am b/Makefrag.am index 8718a94..1607f3c 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -75,8 +75,6 @@ x15_SOURCES += \ kern/semaphore.c \ kern/semaphore.h \ kern/semaphore_i.h \ - kern/shell.c \ - kern/shell.h \ kern/sleepq.c \ kern/sleepq.h \ kern/spinlock.c \ @@ -105,6 +103,12 @@ x15_SOURCES += \ kern/xcall.c \ kern/xcall.h +if X15_SHELL +x15_SOURCES += \ + kern/shell.c \ + kern/shell.h +endif X15_SHELL + x15_SOURCES += \ vm/vm_adv.h \ vm/vm_inherit.h \ diff --git a/configure.ac b/configure.ac index be21f3d..5412c00 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,10 @@ AC_ARG_ENABLE([mutex-pi], (note that priority inheritance is always enabled for real-time mutexes)])]) +AC_ARG_ENABLE([shell], + [AS_HELP_STRING([--enable-shell], + [enable the diagnostics shell])]) + AC_ARG_ENABLE([thread-stack-guard], [AS_HELP_STRING([--enable-thread-stack-guard], [enable kernel thread stack guard pages])]) @@ -104,6 +108,12 @@ AS_IF([test x"$enable_mutex_pi" = xyes], [AC_DEFINE_UNQUOTED([X15_MUTEX_PI], [], [Enable priority inheritance for regular mutexes])]) +AM_CONDITIONAL([X15_SHELL], + [test x"$enable_shell" = xyes]) +AS_IF([test x"$enable_shell" = xyes], + [AC_DEFINE_UNQUOTED([X15_SHELL], [], + [Enable the diagnostics shell])]) + AS_IF([test x"$enable_thread_stack_guard" = xyes], [AC_DEFINE_UNQUOTED([X15_THREAD_STACK_GUARD], [], [Enable the use of guard pages for thread stacks])]) diff --git a/kern/log.c b/kern/log.c index b802909..6adc378 100644 --- a/kern/log.c +++ b/kern/log.c @@ -267,6 +267,8 @@ log_run(void *arg) } } +#ifdef X15_SHELL + static void log_dump(unsigned int level) { @@ -315,14 +317,6 @@ log_shell_dump(int argc, char **argv) log_dump(level); } -void __init -log_setup(void) -{ - cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); - spinlock_init(&log_lock); - log_print_level = LOG_INFO; -} - static struct shell_cmd log_shell_cmds[] = { SHELL_CMD_INITIALIZER2("log_dump", log_shell_dump, "log_dump []", @@ -339,6 +333,16 @@ static struct shell_cmd log_shell_cmds[] = { " 7: debug"), }; +#endif /* X15_SHELL */ + +void __init +log_setup(void) +{ + cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); + spinlock_init(&log_lock); + log_print_level = LOG_INFO; +} + void __init log_start(void) { diff --git a/kern/shell.h b/kern/shell.h index 10bc8a3..81be314 100644 --- a/kern/shell.h +++ b/kern/shell.h @@ -24,6 +24,8 @@ #include #include +#ifdef X15_SHELL + #define SHELL_REGISTER_CMDS(cmds) \ MACRO_BEGIN \ size_t ___i; \ @@ -85,4 +87,10 @@ void shell_start(void); */ int shell_cmd_register(struct shell_cmd *cmd); +#else /* X15_SHELL */ +#define SHELL_REGISTER_CMDS(cmds) +#define shell_setup() +#define shell_start() +#endif /* X15_SHELL */ + #endif /* _KERN_SHELL_H */ diff --git a/kern/task.c b/kern/task.c index d9c9f11..a528199 100644 --- a/kern/task.c +++ b/kern/task.c @@ -64,6 +64,8 @@ task_init(struct task *task, const char *name, struct vm_map *map) strlcpy(task->name, name, sizeof(task->name)); } +#ifdef X15_SHELL + static void task_shell_info(int argc, char *argv[]) { @@ -97,6 +99,8 @@ static struct shell_cmd task_shell_cmds[] = { "print tasks and threads"), }; +#endif /* X15_SHELL */ + void __init task_setup(void) { diff --git a/kern/thread.c b/kern/thread.c index a753eb5..dd0d709 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2229,6 +2229,8 @@ thread_setup_runq(struct thread_runq *runq) thread_setup_idler(runq); } +#ifdef X15_SHELL + /* * This function is meant for debugging only. As a result, it uses a weak * locking policy which allows tracing threads which state may mutate during @@ -2279,6 +2281,8 @@ static struct shell_cmd thread_shell_cmds[] = { "print the stack trace of a given thread"), }; +#endif /* X15_SHELL */ + void __init thread_setup(void) { -- cgit v1.2.3 From 61df7f19dfe3b56abf6c4d00589414c60716ac2b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 11 Jun 2017 11:44:13 +0200 Subject: kern/log: serialize the start of the log and shell threads --- kern/kernel.c | 2 -- kern/log.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/kern/kernel.c b/kern/kernel.c index 30dff08..f41956e 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -52,7 +51,6 @@ kernel_main(void) llsync_setup(); sref_setup(); vm_page_info(); - shell_start(); log_start(); #ifdef X15_RUN_TEST_MODULE diff --git a/kern/log.c b/kern/log.c index 6adc378..a3b34f3 100644 --- a/kern/log.c +++ b/kern/log.c @@ -228,11 +228,13 @@ log_run(void *arg) { unsigned long flags, index, nr_overruns; struct log_record record; + bool start_shell; int error; (void)arg; nr_overruns = 0; + start_shell = true; spinlock_lock_intr_save(&log_lock, &flags); @@ -240,6 +242,16 @@ log_run(void *arg) for (;;) { while (index == cbuf_end(&log_cbuf)) { + /* + * Starting the shell after the log thread sleeps for the first + * time cleanly serializes log messages and shell prompt, making + * a clean ordered output. + */ + if (start_shell) { + shell_start(); + start_shell = false; + } + thread_sleep(&log_lock, &log_cbuf, "log_cbuf"); } -- cgit v1.2.3 From 72ed0dc2f153e7cf1d6e96f86a773bbe490e9e1c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 13 Jun 2017 21:23:50 +0200 Subject: Various atomic access fixes --- kern/atomic.h | 3 --- kern/bitmap.h | 14 ++++++++++++-- kern/bitmap_i.h | 2 ++ kern/rtmutex.c | 4 ++-- kern/sref.c | 10 +++++----- kern/thread_i.h | 4 ++-- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/kern/atomic.h b/kern/atomic.h index 63f0ac7..6c0105d 100644 --- a/kern/atomic.h +++ b/kern/atomic.h @@ -135,7 +135,4 @@ MACRO_END #define atomic_fetch_sub_acq_rel(ptr, val) \ atomic_fetch_sub(ptr, val, ATOMIC_ACQ_REL) -#define atomic_or_acq_rel(ptr, val) atomic_or(ptr, val, ATOMIC_ACQ_REL) -#define atomic_and_acq_rel(ptr, val) atomic_and(ptr, val, ATOMIC_ACQ_REL) - #endif /* _KERN_ATOMIC_H */ diff --git a/kern/bitmap.h b/kern/bitmap.h index a604d17..c206944 100644 --- a/kern/bitmap.h +++ b/kern/bitmap.h @@ -78,7 +78,7 @@ bitmap_set_atomic(unsigned long *bm, int bit) bitmap_lookup(bm, bit); } - atomic_or_acq_rel(bm, bitmap_mask(bit)); + atomic_or(bm, bitmap_mask(bit), ATOMIC_RELEASE); } static inline void @@ -98,7 +98,7 @@ bitmap_clear_atomic(unsigned long *bm, int bit) bitmap_lookup(bm, bit); } - atomic_and_acq_rel(bm, ~bitmap_mask(bit)); + atomic_and(bm, ~bitmap_mask(bit), ATOMIC_RELEASE); } static inline int @@ -111,6 +111,16 @@ bitmap_test(const unsigned long *bm, int bit) return ((*bm & bitmap_mask(bit)) != 0); } +static inline int +bitmap_test_atomic(const unsigned long *bm, int bit) +{ + if (bit >= LONG_BIT) { + bitmap_lookup(bm, bit); + } + + return ((atomic_load(bm, ATOMIC_ACQUIRE) & bitmap_mask(bit)) != 0); +} + static inline void bitmap_and(unsigned long *a, const unsigned long *b, int nr_bits) { diff --git a/kern/bitmap_i.h b/kern/bitmap_i.h index 9b79d3c..11c23eb 100644 --- a/kern/bitmap_i.h +++ b/kern/bitmap_i.h @@ -29,6 +29,8 @@ * to a bit inside the word pointed by the former. * * Implemented as a macro for const-correctness. + * + * TODO Fix interface. */ #define bitmap_lookup(bm, bit) \ MACRO_BEGIN \ diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 09f011c..e78c389 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -29,7 +29,7 @@ static void rtmutex_set_contended(struct rtmutex *rtmutex) { - atomic_or_acq_rel(&rtmutex->owner, RTMUTEX_CONTENDED); + atomic_or(&rtmutex->owner, RTMUTEX_CONTENDED, ATOMIC_RELEASE); } void @@ -64,7 +64,7 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) turnstile_own(turnstile); if (turnstile_empty(turnstile)) { - prev_owner = atomic_swap_acquire(&rtmutex->owner, owner); + prev_owner = atomic_swap(&rtmutex->owner, owner, ATOMIC_RELAXED); assert(prev_owner == (owner | bits)); } diff --git a/kern/sref.c b/kern/sref.c index 5aad8da..bc3f9e9 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -259,13 +259,13 @@ sref_weakref_init(struct sref_weakref *weakref, struct sref_counter *counter) static void sref_weakref_mark_dying(struct sref_weakref *weakref) { - atomic_or_acq_rel(&weakref->addr, SREF_WEAKREF_DYING); + atomic_or(&weakref->addr, SREF_WEAKREF_DYING, ATOMIC_RELEASE); } static void sref_weakref_clear_dying(struct sref_weakref *weakref) { - atomic_and_acq_rel(&weakref->addr, SREF_WEAKREF_MASK); + atomic_and(&weakref->addr, SREF_WEAKREF_MASK, ATOMIC_RELEASE); } static int @@ -273,8 +273,8 @@ sref_weakref_kill(struct sref_weakref *weakref) { uintptr_t addr, oldval; - addr = weakref->addr | SREF_WEAKREF_DYING; - oldval = atomic_cas_release(&weakref->addr, addr, (uintptr_t)NULL); + addr = atomic_load(&weakref->addr, ATOMIC_RELAXED) | SREF_WEAKREF_DYING; + oldval = atomic_cas(&weakref->addr, addr, (uintptr_t)NULL, ATOMIC_RELAXED); if (oldval != addr) { assert((oldval & SREF_WEAKREF_MASK) == (addr & SREF_WEAKREF_MASK)); @@ -290,7 +290,7 @@ sref_weakref_tryget(struct sref_weakref *weakref) uintptr_t addr, oldval, newval; do { - addr = weakref->addr; + addr = atomic_load(&weakref->addr, ATOMIC_RELAXED); newval = addr & SREF_WEAKREF_MASK; oldval = atomic_cas_acquire(&weakref->addr, addr, newval); } while (oldval != addr); diff --git a/kern/thread_i.h b/kern/thread_i.h index 2e1b88a..6f08c33 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -195,13 +195,13 @@ void thread_destroy(struct thread *thread); static inline void thread_set_flag(struct thread *thread, unsigned long flag) { - atomic_or_acq_rel(&thread->flags, flag); + atomic_or(&thread->flags, flag, ATOMIC_RELEASE); } static inline void thread_clear_flag(struct thread *thread, unsigned long flag) { - atomic_and_acq_rel(&thread->flags, ~flag); + atomic_and(&thread->flags, ~flag, ATOMIC_RELEASE); } static inline int -- cgit v1.2.3 From 3350443b4a800a16f4663e0573903633ad82225f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 13 Jun 2017 23:26:38 +0200 Subject: x86: improve TCB load and context switch --- arch/x86/machine/tcb.c | 62 ++++++++++++++++++++++++++++++++++++---------- arch/x86/machine/tcb.h | 21 +++++++--------- arch/x86/machine/tcb_asm.S | 60 ++++++++++++++++---------------------------- kern/thread.c | 10 +++----- kern/thread.h | 7 ++++++ kern/thread_i.h | 4 --- 6 files changed, 91 insertions(+), 73 deletions(-) diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index df1b222..5d16ea2 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -24,18 +24,58 @@ #include #include -/* - * Low level functions. - */ void __noreturn tcb_context_load(struct tcb *tcb); void __noreturn tcb_start(void); +void tcb_context_restore(void); struct tcb *tcb_current_ptr __percpu; +static void +tcb_stack_push(struct tcb *tcb, uintptr_t word) +{ + uintptr_t *ptr; + + ptr = (uintptr_t *)tcb->sp; + ptr--; + *ptr = word; + tcb->sp = (uintptr_t)ptr; +} + +#ifdef __LP64__ + +static void +tcb_stack_forge(struct tcb *tcb, void (*fn)(void *), void *arg) +{ + tcb_stack_push(tcb, (uintptr_t)arg); + tcb_stack_push(tcb, (uintptr_t)fn); + tcb_stack_push(tcb, (uintptr_t)tcb_start); /* Return address */ + tcb_stack_push(tcb, CPU_EFL_ONE); /* RFLAGS */ + tcb_stack_push(tcb, 0); /* RBX */ + tcb_stack_push(tcb, 0); /* R12 */ + tcb_stack_push(tcb, 0); /* R13 */ + tcb_stack_push(tcb, 0); /* R14 */ + tcb_stack_push(tcb, 0); /* R15 */ +} + +#else /* __LP64__ */ + +static void +tcb_stack_forge(struct tcb *tcb, void (*fn)(void *), void *arg) +{ + tcb_stack_push(tcb, (uintptr_t)arg); + tcb_stack_push(tcb, (uintptr_t)fn); + tcb_stack_push(tcb, (uintptr_t)tcb_start); /* Return address */ + tcb_stack_push(tcb, CPU_EFL_ONE); /* EFLAGS */ + tcb_stack_push(tcb, 0); /* EBX */ + tcb_stack_push(tcb, 0); /* EDI */ + tcb_stack_push(tcb, 0); /* ESI */ +} + +#endif /* __LP64__ */ + int -tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)) +tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) { - void **ptr; int error; error = pmap_thread_init(thread_from_tcb(tcb)); @@ -45,12 +85,8 @@ tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)) } tcb->bp = 0; - tcb->sp = (unsigned long)stack + STACK_SIZE - sizeof(unsigned long); - tcb->ip = (unsigned long)tcb_start; - - ptr = (void **)tcb->sp; - *ptr = fn; - + tcb->sp = (uintptr_t)stack + STACK_SIZE; + tcb_stack_forge(tcb, fn, arg); return 0; } @@ -66,5 +102,5 @@ tcb_load(struct tcb *tcb) void tcb_trace(const struct tcb *tcb) { - strace_show(tcb->ip, tcb->bp); + strace_show((uintptr_t)tcb_context_restore, tcb->bp); } diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 914ec9d..0f67b76 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -22,6 +22,7 @@ #define _X86_TCB_H #include +#include #include #include @@ -30,25 +31,19 @@ * Thread control block. */ struct tcb { - unsigned long bp; - unsigned long sp; - unsigned long ip; + uintptr_t bp; + uintptr_t sp; }; /* * Initialize a TCB. * * Prepare the given stack for execution. The context is defined so that it - * will call fn() with interrupts disabled when loaded. + * will call thread_main(fn, arg) with interrupts disabled when loaded. * * In addition, initialize any thread-local machine-specific data. */ -int tcb_init(struct tcb *tcb, void *stack, void (*fn)(void)); - -/* - * Low level context switch function. - */ -void tcb_context_switch(struct tcb *prev, struct tcb *next); +int tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg); static inline struct tcb * tcb_current(void) @@ -79,6 +74,8 @@ void __noreturn tcb_load(struct tcb *tcb); static inline void tcb_switch(struct tcb *prev, struct tcb *next) { + void tcb_context_switch(struct tcb *prev, struct tcb *next); + assert(!cpu_intr_enabled()); tcb_set_current(next); @@ -88,7 +85,7 @@ tcb_switch(struct tcb *prev, struct tcb *next) /* * Dump the stack trace of a TCB. * - * The thread associated to the TCB should not be running. + * The thread associated to the TCB must not be running. */ void tcb_trace(const struct tcb *tcb); diff --git a/arch/x86/machine/tcb_asm.S b/arch/x86/machine/tcb_asm.S index e4e2c45..7c9140f 100644 --- a/arch/x86/machine/tcb_asm.S +++ b/arch/x86/machine/tcb_asm.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -24,24 +24,18 @@ #ifdef __LP64__ ASM_ENTRY(tcb_context_load) - movq (%rdi), %rbp /* load frame pointer from TCB */ - movq 8(%rdi), %rsp /* load stack pointer from TCB */ - movq 16(%rdi), %rax /* load instruction pointer from TCB */ - pushq $CPU_EFL_ONE /* prepare new RFLAGS register value */ - popfq /* load value into RFLAGS register */ - jmp *%rax /* branch to loaded instruction pointer */ + movq (%rdi), %rbp /* load frame pointer from TCB */ + movq 8(%rdi), %rsp /* load stack pointer from TCB */ + jmp tcb_context_restore ASM_END(tcb_context_load) #else /* __LP64__ */ ASM_ENTRY(tcb_context_load) - movl 4(%esp), %eax /* load TCB address */ - movl (%eax), %ebp /* load frame pointer from TCB */ - movl 4(%eax), %esp /* load stack pointer from TCB */ - movl 8(%eax), %ecx /* load instruction pointer from TCB */ - pushl $CPU_EFL_ONE /* prepare new EFLAGS register value */ - popfl /* load value into EFLAGS register */ - jmp *%ecx /* branch to loaded instruction pointer */ + movl 4(%esp), %eax /* load TCB address */ + movl (%eax), %ebp /* load frame pointer from TCB */ + movl 4(%eax), %esp /* load stack pointer from TCB */ + jmp tcb_context_restore ASM_END(tcb_context_load) #endif /* __LP64__ */ @@ -51,13 +45,13 @@ ASM_END(tcb_context_load) #ifdef __LP64__ ASM_ENTRY(tcb_start) - popq %rax /* load function passed at TCB initialization (this - makes the stack pointer reach the stack top) */ - call *%rax /* branch to loaded function, pushing the return - address to start a clean stack trace */ + popq %rdi /* load function */ + popq %rsi /* load argument */ + call thread_main /* use the call instruction to start + a clean stack trace */ /* Never reached */ - nop /* Make the return address point to an instruction + nop /* make the return address point to an instruction inside the function to build a clean stack trace */ ASM_END(tcb_start) @@ -70,19 +64,12 @@ ASM_ENTRY(tcb_context_switch) pushq %r15 movq %rbp, (%rdi) /* store frame pointer into prev TCB */ movq %rsp, 8(%rdi) /* store stack pointer into prev TCB */ - movq $1f, 16(%rdi) /* store next instruction address into prev TCB */ movq (%rsi), %rbp /* load frame pointer from next TCB */ movq 8(%rsi), %rsp /* load stack pointer from next TCB */ - movq 16(%rsi), %rax /* load instruction pointer from next TCB */ - jmp *%rax /* branch to loaded instruction pointer */ -/* - * This code is run on context restoration. The frame and stack pointers - * have already been loaded to their correct values. Load registers which - * were stored on the stack when the context was saved and return. - */ -1: - popq %r15 +.global tcb_context_restore +tcb_context_restore: + popq %r15 /* load registers as required by ABI */ popq %r14 popq %r13 popq %r12 @@ -94,13 +81,12 @@ ASM_END(tcb_context_switch) #else /* __LP64__ */ ASM_ENTRY(tcb_start) - popl %eax /* load function passed at TCB initialization (this - makes the stack pointer reach the stack top) */ - call *%eax /* branch to loaded function, pushing the return - address to start a clean stack trace */ + call thread_main /* the stack already contains the expected arguments + in the expected order, use the call instruction to + start a clean stack trace */ /* Never reached */ - nop /* Make the return address point to an instruction + nop /* make the return address point to an instruction inside the function to build a clean stack trace */ ASM_END(tcb_start) @@ -113,18 +99,16 @@ ASM_ENTRY(tcb_context_switch) pushl %esi movl %ebp, (%eax) /* store frame pointer into prev TCB */ movl %esp, 4(%eax) /* store stack pointer into prev TCB */ - movl $1f, 8(%eax) /* store next instruction address into prev TCB */ movl (%ecx), %ebp /* load frame pointer from next TCB */ movl 4(%ecx), %esp /* load stack pointer from next TCB */ - movl 8(%ecx), %edx /* load instruction pointer from next TCB */ - jmp *%edx /* branch to loaded instruction pointer */ /* * This code is run on context restoration. The frame and stack pointers * have already been loaded to their correct values. Load registers which * were stored on the stack when the context was saved and return. */ -1: +.global tcb_context_restore +tcb_context_restore: popl %esi popl %edi popl %ebx diff --git a/kern/thread.c b/kern/thread.c index dd0d709..8111c25 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1727,8 +1727,8 @@ thread_ap_bootstrap(void) tcb_set_current(&thread_booters[cpu_id()].tcb); } -static void -thread_main(void) +void +thread_main(void (*fn)(void *), void *arg) { struct thread *thread; @@ -1742,7 +1742,7 @@ thread_main(void) cpu_intr_enable(); thread_preempt_enable(); - thread->fn(thread->arg); + fn(arg); thread_exit(); } @@ -1829,14 +1829,12 @@ thread_init(struct thread *thread, void *stack, thread->task = task; thread->stack = stack; strlcpy(thread->name, attr->name, sizeof(thread->name)); - thread->fn = fn; - thread->arg = arg; if (attr->flags & THREAD_ATTR_DETACHED) { thread->flags |= THREAD_DETACHED; } - error = tcb_init(&thread->tcb, stack, thread_main); + error = tcb_init(&thread->tcb, stack, fn, arg); if (error) { goto error_tcb; diff --git a/kern/thread.h b/kern/thread.h index 144735d..f41daef 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -170,6 +170,13 @@ thread_attr_set_priority(struct thread_attr *attr, unsigned short priority) void thread_bootstrap(void); void thread_ap_bootstrap(void); +/* + * Thread entry point. + * + * Loaded TCBs are expected to call this function with interrupts disabled. + */ +void thread_main(void (*fn)(void *), void *arg); + /* * Initialize the thread module. */ diff --git a/kern/thread_i.h b/kern/thread_i.h index 6f08c33..9c8d4fd 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -178,10 +178,6 @@ struct thread { struct list task_node; /* (T) */ void *stack; /* (-) */ char name[THREAD_NAME_SIZE]; /* ( ) */ - - /* TODO Move out of the structure and make temporary */ - void (*fn)(void *); - void *arg; } __aligned(CPU_L1_SIZE); #define THREAD_ATTR_DETACHED 0x1 -- cgit v1.2.3 From 9f523da9e60aa8b8748dd320b04d8e42a5209668 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 01:00:02 +0200 Subject: Initialize the shell module later --- arch/x86/machine/boot.c | 2 -- kern/kernel.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 5f27d90..59f0d67 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -58,7 +58,6 @@ #include #include #include -#include #include #include #include @@ -494,7 +493,6 @@ boot_main(void) atcons_bootstrap(); uart_bootstrap(); printf_setup(); - shell_setup(); uart_info(); pmap_bootstrap(); sref_bootstrap(); diff --git a/kern/kernel.c b/kern/kernel.c index f41956e..f079f9a 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ kernel_main(void) assert(!cpu_intr_enabled()); percpu_cleanup(); + shell_setup(); cpumap_setup(); xcall_setup(); task_setup(); -- cgit v1.2.3 From b4c2ce830188d0bd8bb84014a623ecb06c160ed9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 01:07:44 +0200 Subject: kern/syscnt: new syscnt_info shell command --- kern/kernel.c | 2 ++ kern/syscnt.c | 27 +++++++++++++++++++++++++++ kern/syscnt.h | 5 +++++ 3 files changed, 34 insertions(+) diff --git a/kern/kernel.c b/kern/kernel.c index f079f9a..326c70a 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ kernel_main(void) percpu_cleanup(); shell_setup(); + syscnt_register_shell_cmds(); cpumap_setup(); xcall_setup(); task_setup(); diff --git a/kern/syscnt.c b/kern/syscnt.c index 5b479f3..2e042bc 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,32 @@ syscnt_setup(void) mutex_init(&syscnt_lock); } +#ifdef X15_SHELL + +static void +syscnt_shell_info(int argc, char **argv) +{ + char *prefix; + + prefix = (argc >= 2) ? argv[1] : NULL; + syscnt_info(prefix); +} + + +static struct shell_cmd syscnt_shell_cmds[] = { + SHELL_CMD_INITIALIZER("syscnt_info", syscnt_shell_info, + "syscnt_info []", + "Show information about system counters"), +}; + +#endif /* X15_SHELL */ + +void __init +syscnt_register_shell_cmds(void) +{ + SHELL_REGISTER_CMDS(syscnt_shell_cmds); +} + void __init syscnt_register(struct syscnt *syscnt, const char *name) { diff --git a/kern/syscnt.h b/kern/syscnt.h index a4bae3d..35a291d 100644 --- a/kern/syscnt.h +++ b/kern/syscnt.h @@ -57,6 +57,11 @@ void syscnt_setup(void); */ void syscnt_register(struct syscnt *syscnt, const char *name); +/* + * Register shell commands. + */ +void syscnt_register_shell_cmds(void); + #ifdef ATOMIC_HAVE_64B_OPS static inline void -- cgit v1.2.3 From b28eec463fa370b4778a3274e4196fade4bab54b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 01:29:30 +0200 Subject: kern/syscnt: fix syscnt_info help description --- kern/syscnt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/syscnt.c b/kern/syscnt.c index 2e042bc..a9c13f8 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -54,7 +54,7 @@ syscnt_shell_info(int argc, char **argv) static struct shell_cmd syscnt_shell_cmds[] = { SHELL_CMD_INITIALIZER("syscnt_info", syscnt_shell_info, "syscnt_info []", - "Show information about system counters"), + "show information about system counters"), }; #endif /* X15_SHELL */ -- cgit v1.2.3 From b7fa434992d4eb5e871b8ebee43ba4514af788bf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 21:45:19 +0200 Subject: configure.ac: emit notice when enabling the shell --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5412c00..ca434a5 100644 --- a/configure.ac +++ b/configure.ac @@ -112,7 +112,8 @@ AM_CONDITIONAL([X15_SHELL], [test x"$enable_shell" = xyes]) AS_IF([test x"$enable_shell" = xyes], [AC_DEFINE_UNQUOTED([X15_SHELL], [], - [Enable the diagnostics shell])]) + [Enable the diagnostics shell]) + AC_MSG_NOTICE([diagnostics shell enabled])]) AS_IF([test x"$enable_thread_stack_guard" = xyes], [AC_DEFINE_UNQUOTED([X15_THREAD_STACK_GUARD], [], -- cgit v1.2.3 From 017d30aa5858375274a3a636e5ece1bdd1d1fb57 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 23:58:22 +0200 Subject: configure.ac: report whether build options are enabled or not --- configure.ac | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index ca434a5..57f07d1 100644 --- a/configure.ac +++ b/configure.ac @@ -51,15 +51,21 @@ AC_ARG_ENABLE([mutex-pi], [AS_HELP_STRING([--enable-mutex-pi], [enable priority inheritance for regular mutexes (note that priority inheritance is always - enabled for real-time mutexes)])]) + enabled for real-time mutexes)])], + [], + [enable_mutex_pi=no]) AC_ARG_ENABLE([shell], [AS_HELP_STRING([--enable-shell], - [enable the diagnostics shell])]) + [enable the diagnostics shell])], + [], + [enable_shell=no]) AC_ARG_ENABLE([thread-stack-guard], [AS_HELP_STRING([--enable-thread-stack-guard], - [enable kernel thread stack guard pages])]) + [enable kernel thread stack guard pages])], + [], + [enable_thread_stack_guard=no]) AC_DEFINE([__KERNEL__], [1], [kernel code]) AC_DEFINE_UNQUOTED([X15_ARCH], [$arch], [arch]) @@ -107,17 +113,19 @@ AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) AS_IF([test x"$enable_mutex_pi" = xyes], [AC_DEFINE_UNQUOTED([X15_MUTEX_PI], [], [Enable priority inheritance for regular mutexes])]) +AC_MSG_NOTICE([priority inheritance for regular mutexes: $enable_mutex_pi]) AM_CONDITIONAL([X15_SHELL], [test x"$enable_shell" = xyes]) AS_IF([test x"$enable_shell" = xyes], [AC_DEFINE_UNQUOTED([X15_SHELL], [], - [Enable the diagnostics shell]) - AC_MSG_NOTICE([diagnostics shell enabled])]) + [Enable the diagnostics shell])]) +AC_MSG_NOTICE([diagnostics shell: $enable_shell]) AS_IF([test x"$enable_thread_stack_guard" = xyes], [AC_DEFINE_UNQUOTED([X15_THREAD_STACK_GUARD], [], [Enable the use of guard pages for thread stacks])]) +AC_MSG_NOTICE([thread stack guard pages: $enable_thread_stack_guard]) AH_BOTTOM([#include ]) AC_CONFIG_HEADER([config.h]) -- cgit v1.2.3 From e38ce312ae1cd1cb3978e9207ffa9a951ad592ad Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 23:58:45 +0200 Subject: INSTALL: document the --enable-shell option --- INSTALL | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL b/INSTALL index 1a34ecd..3ec3eca 100644 --- a/INSTALL +++ b/INSTALL @@ -249,6 +249,10 @@ X15 Options likely to measurably reduce overall performance. It should only be enabled if you know what you're doing. +`--enable-shell' + Enable the diagnostics shell. Note that some shell commands may have + a real-time unsafe behaviour. + `--enable-thread-stack-guard' Enable the use of guard pages around kernel thread stacks to catch overflows. Note that this feature wastes precious kernel virtual -- cgit v1.2.3 From d8ed3b6cda0a7dbc32a6ca2fecfc71a6fb1fe67c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 14 Jun 2017 23:59:01 +0200 Subject: x86/configure.ac: unconditionally report whether PAE is enabled --- arch/x86/configfrag.ac | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/x86/configfrag.ac b/arch/x86/configfrag.ac index 0ce93e0..0007831 100644 --- a/arch/x86/configfrag.ac +++ b/arch/x86/configfrag.ac @@ -1,6 +1,5 @@ m4_define([x86_ENABLE_PAE], - [AC_DEFINE([X15_X86_PAE], [1], [use PAE page translation]) - AC_MSG_NOTICE([physical address extension enabled])]) + [AC_DEFINE([X15_X86_PAE], [1], [use PAE page translation])]) m4_define([x86_SELECT_I386], [machine=i386 @@ -27,7 +26,9 @@ m4_define([x86_SELECT], AC_ARG_ENABLE([pae], [AS_HELP_STRING([--enable-pae], - [enable physical address extension (i386 only)])]) + [enable physical address extension (i386 only)])], + [], + [enable_pae=no]) AS_IF([test x"$opt_i386$opt_amd64" = xyesyes], [AC_MSG_ERROR([select only one of i386 or amd64])], @@ -48,7 +49,8 @@ m4_define([x86_SELECT], -mno-avx]) AC_DEFINE_UNQUOTED([X15_X86_MACHINE], [$machine], [machine]) - AC_MSG_NOTICE([machine type: $arch ($machine)])]) + AC_MSG_NOTICE([machine type: $arch ($machine)]) + AC_MSG_NOTICE([physical address extension: $enable_pae])]) AS_CASE(["$host_cpu"], [i?86|x86_64], [x86_SELECT]) -- cgit v1.2.3 From 0eac4af899270f4ece438a2eb05c186639a9faee Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 00:29:00 +0200 Subject: kern/macros: remove the __format_printf macro This macro isn't used enough for this alias to have any real value. --- kern/macros.h | 3 --- kern/panic.h | 3 ++- kern/printf.h | 8 +++++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kern/macros.h b/kern/macros.h index a5b7b03..8f0d10c 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -73,7 +73,4 @@ #define __packed __attribute__((packed)) #define __alias(x) __attribute__((alias(x))) -#define __format_printf(fmt, args) \ - __attribute__((format(printf, fmt, args))) - #endif /* _KERN_MACROS_H */ diff --git a/kern/panic.h b/kern/panic.h index 9d1d31e..cd0651a 100644 --- a/kern/panic.h +++ b/kern/panic.h @@ -23,6 +23,7 @@ /* * Print the given message and halt the system immediately. */ -void __noreturn panic(const char *format, ...) __format_printf(1, 2); +void __noreturn panic(const char *format, ...) + __attribute__((format(printf, 1, 2))); #endif /* _KERN_PANIC_H */ diff --git a/kern/printf.h b/kern/printf.h index 6643d66..0aed720 100644 --- a/kern/printf.h +++ b/kern/printf.h @@ -33,10 +33,12 @@ #include -#include +int printf(const char *format, ...) + __attribute__((format(printf, 1, 2))); + +int vprintf(const char *format, va_list ap) + __attribute__((format(printf, 1, 0))); -int printf(const char *format, ...) __format_printf(1, 2); -int vprintf(const char *format, va_list ap) __format_printf(1, 0); void printf_setup(void); #endif /* _KERN_PRINTF_H */ -- cgit v1.2.3 From 03a3851ce27aefddc94e7b789eb160144a7719e1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 00:47:30 +0200 Subject: kern/fmt: include kern/macros.h --- kern/fmt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kern/fmt.c b/kern/fmt.c index 1c635e1..2adec7f 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -38,6 +38,7 @@ #include #include +#include #include /* -- cgit v1.2.3 From 2f3d23605b841f9e4f4c0a13c69a9ff2db033903 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 00:54:59 +0200 Subject: x86/configure.ac: report PAE status only for i386 --- arch/x86/configfrag.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/configfrag.ac b/arch/x86/configfrag.ac index 0007831..1d37a0e 100644 --- a/arch/x86/configfrag.ac +++ b/arch/x86/configfrag.ac @@ -50,7 +50,8 @@ m4_define([x86_SELECT], AC_DEFINE_UNQUOTED([X15_X86_MACHINE], [$machine], [machine]) AC_MSG_NOTICE([machine type: $arch ($machine)]) - AC_MSG_NOTICE([physical address extension: $enable_pae])]) + AS_IF([test x"$machine" = xi386], + [AC_MSG_NOTICE([physical address extension: $enable_pae])])]) AS_CASE(["$host_cpu"], [i?86|x86_64], [x86_SELECT]) -- cgit v1.2.3 From f7dd911ee2c78b9fc856e88afae8edc101b25844 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 22:15:31 +0200 Subject: kern/bitmap: make bitmap_lookup conform to coding rules Macros with lower case names must have a function-like interface. --- kern/bitmap.c | 2 +- kern/bitmap.h | 12 ++++++------ kern/bitmap_i.h | 10 ++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/kern/bitmap.c b/kern/bitmap.c index 0e13ef5..97e497d 100644 --- a/kern/bitmap.c +++ b/kern/bitmap.c @@ -78,7 +78,7 @@ bitmap_find_next_bit(const unsigned long *bm, int nr_bits, int bit, end = bm + BITMAP_LONGS(nr_bits); if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); nr_bits -= ((bm - start) * LONG_BIT); } diff --git a/kern/bitmap.h b/kern/bitmap.h index c206944..9a3d7a0 100644 --- a/kern/bitmap.h +++ b/kern/bitmap.h @@ -65,7 +65,7 @@ static inline void bitmap_set(unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } *bm |= bitmap_mask(bit); @@ -75,7 +75,7 @@ static inline void bitmap_set_atomic(unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } atomic_or(bm, bitmap_mask(bit), ATOMIC_RELEASE); @@ -85,7 +85,7 @@ static inline void bitmap_clear(unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } *bm &= ~bitmap_mask(bit); @@ -95,7 +95,7 @@ static inline void bitmap_clear_atomic(unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } atomic_and(bm, ~bitmap_mask(bit), ATOMIC_RELEASE); @@ -105,7 +105,7 @@ static inline int bitmap_test(const unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } return ((*bm & bitmap_mask(bit)) != 0); @@ -115,7 +115,7 @@ static inline int bitmap_test_atomic(const unsigned long *bm, int bit) { if (bit >= LONG_BIT) { - bitmap_lookup(bm, bit); + bitmap_lookup(&bm, &bit); } return ((atomic_load(bm, ATOMIC_ACQUIRE) & bitmap_mask(bit)) != 0); diff --git a/kern/bitmap_i.h b/kern/bitmap_i.h index 11c23eb..dc91a0a 100644 --- a/kern/bitmap_i.h +++ b/kern/bitmap_i.h @@ -29,16 +29,14 @@ * to a bit inside the word pointed by the former. * * Implemented as a macro for const-correctness. - * - * TODO Fix interface. */ -#define bitmap_lookup(bm, bit) \ +#define bitmap_lookup(bmp, bitp) \ MACRO_BEGIN \ int i; \ \ - i = BITMAP_LONGS((bit) + 1) - 1; \ - (bm) += i; \ - (bit) -= i * LONG_BIT; \ + i = BITMAP_LONGS(*(bitp) + 1) - 1; \ + *(bmp) += i; \ + *(bitp) -= i * LONG_BIT; \ MACRO_END static inline unsigned long -- cgit v1.2.3 From ad28762f765cb4ca01f5d495c081f8ccab7c5da6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 22:17:20 +0200 Subject: doc/style(9): fix typo --- doc/style.9.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index 101f340..18ddfa5 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -321,7 +321,7 @@ NAMES First of all, use underscores instead of camel case to separate words in compound names. Function and variable names are in lower case. Macro -names generally are in upper caser, except for macros which behave +names generally are in upper case, except for macros which behave strictly like functions and could be replaced with inline functions with no API change. -- cgit v1.2.3 From 81e8137e8d2c80eef05223dccb7d572be101cf3a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 22:29:42 +0200 Subject: doc/style(9): discuss side-effects and conditions --- doc/style.9.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/style.9.txt b/doc/style.9.txt index 18ddfa5..1f8b23e 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -644,6 +644,12 @@ if (error) { The other exception is the historical use of the *int* type, rather than the C99 *bool* type, for boolean values. +Side effects and conditions +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Conditions must never be made of expressions with side effects. This +completely removes the need to think about short-circuit evaluations. + SEE --- -- cgit v1.2.3 From 8a8afe78bdb41850bff003353ba0a3229b0106b4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 23:11:33 +0200 Subject: x86/tcb: describe stack alignment ABI constraints on amd64 --- arch/x86/machine/tcb_asm.S | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/tcb_asm.S b/arch/x86/machine/tcb_asm.S index 7c9140f..a6e31da 100644 --- a/arch/x86/machine/tcb_asm.S +++ b/arch/x86/machine/tcb_asm.S @@ -47,8 +47,16 @@ ASM_END(tcb_context_load) ASM_ENTRY(tcb_start) popq %rdi /* load function */ popq %rsi /* load argument */ - call thread_main /* use the call instruction to start - a clean stack trace */ + + /* + * Use the call instruction to start a clean stack trace. + * + * Note that, on amd64, the stack must be 16-byte before the call + * instruction, so that "$(rsp + 8) is a multiple is always a multiple + * of 16 when control is transferred to the function entry point", + * which is another reason to use call instead of a bare jump. + */ + call thread_main /* Never reached */ nop /* make the return address point to an instruction -- cgit v1.2.3 From 363d01aa8443bcccab267ff9a1830dcbf67eaba1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 15 Jun 2017 23:31:30 +0200 Subject: kern/thread: prevent stack tracing running threads. --- kern/thread.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kern/thread.c b/kern/thread.c index 8111c25..713c28f 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2238,7 +2238,9 @@ static void thread_shell_trace(int argc, char *argv[]) { const char *task_name, *thread_name; + struct thread_runq *runq; struct thread *thread; + unsigned long flags; struct task *task; int error; @@ -2265,7 +2267,16 @@ thread_shell_trace(int argc, char *argv[]) goto error; } - tcb_trace(&thread->tcb); + runq = thread_lock_runq(thread, &flags); + + if (thread == runq->current) { + printf("thread: trace: thread is running\n"); + } else { + tcb_trace(&thread->tcb); + } + + thread_unlock_runq(runq, flags); + thread_unref(thread); return; -- cgit v1.2.3 From bf32d26490a6fd688194f67856563f0a5a843668 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 17 Jun 2017 18:39:07 +0200 Subject: kern/cbuf: use size_t instead of unsigned long --- kern/cbuf.c | 11 ++++++----- kern/cbuf.h | 20 +++++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index 5c636e5..ef5c7e3 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -16,16 +16,17 @@ */ #include +#include #include #include #include /* Negative close to 0 so that an overflow occurs early */ -#define CBUF_INIT_INDEX ((unsigned long)-500) +#define CBUF_INIT_INDEX ((size_t)-500) void -cbuf_init(struct cbuf *cbuf, char *buf, unsigned long capacity) +cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity) { assert(ISP2(capacity)); @@ -35,8 +36,8 @@ cbuf_init(struct cbuf *cbuf, char *buf, unsigned long capacity) cbuf->end = cbuf->start; } -static unsigned long -cbuf_index(const struct cbuf *cbuf, unsigned long abs_index) +static size_t +cbuf_index(const struct cbuf *cbuf, size_t abs_index) { return abs_index & (cbuf->capacity - 1); } @@ -66,7 +67,7 @@ cbuf_pop(struct cbuf *cbuf, char *bytep) } int -cbuf_read(const struct cbuf *cbuf, unsigned long index, char *bytep) +cbuf_read(const struct cbuf *cbuf, size_t index, char *bytep) { /* Mind integer overflows */ if ((cbuf->end - index - 1) >= cbuf_size(cbuf)) { diff --git a/kern/cbuf.h b/kern/cbuf.h index 3ea3841..b675fd5 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -21,6 +21,8 @@ #ifndef _KERN_CBUF_H #define _KERN_CBUF_H +#include + /* * Circular buffer descriptor. * @@ -29,30 +31,30 @@ */ struct cbuf { char *buf; - unsigned long capacity; - unsigned long start; - unsigned long end; + size_t capacity; + size_t start; + size_t end; }; -static inline unsigned long +static inline size_t cbuf_capacity(const struct cbuf *cbuf) { return cbuf->capacity; } -static inline unsigned long +static inline size_t cbuf_start(const struct cbuf *cbuf) { return cbuf->start; } -static inline unsigned long +static inline size_t cbuf_end(const struct cbuf *cbuf) { return cbuf->end; } -static inline unsigned long +static inline size_t cbuf_size(const struct cbuf *cbuf) { return cbuf->end - cbuf->start; @@ -70,7 +72,7 @@ cbuf_clear(struct cbuf *cbuf) * The descriptor is set to use the given buffer for storage. Capacity * must be a power-of-two. */ -void cbuf_init(struct cbuf *cbuf, char *buf, unsigned long capacity); +void cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity); /* * Append a byte to a circular buffer. @@ -96,6 +98,6 @@ int cbuf_pop(struct cbuf *cbuf, char *bytep); * Otherwise the byte is stored at the bytep address and 0 is returned. * The buffer isn't changed by this operation. */ -int cbuf_read(const struct cbuf *cbuf, unsigned long index, char *bytep); +int cbuf_read(const struct cbuf *cbuf, size_t index, char *bytep); #endif /* _KERN_CBUF_H */ -- cgit v1.2.3 From 0a5af0a62ba2ed1c17404b1dd8ed15b0ff41abf7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 17 Jun 2017 18:45:31 +0200 Subject: kern/cbuf: implement buffered reads and writes This change brings an interface for fast buffered accesses to the content of a circular buffer, and also an interface to write into a circular buffer at custom locations, in exchange for a small interface break of cbuf_read. --- kern/cbuf.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- kern/cbuf.h | 21 ++++++++++++--- kern/log.c | 5 ++-- 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index ef5c7e3..83032e5 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -42,18 +43,23 @@ cbuf_index(const struct cbuf *cbuf, size_t abs_index) return abs_index & (cbuf->capacity - 1); } -void -cbuf_push(struct cbuf *cbuf, char byte) +static void +cbuf_update_start(struct cbuf *cbuf) { - cbuf->buf[cbuf_index(cbuf, cbuf->end)] = byte; - cbuf->end++; - /* Mind integer overflows */ if (cbuf_size(cbuf) > cbuf->capacity) { cbuf->start = cbuf->end - cbuf->capacity; } } +void +cbuf_push(struct cbuf *cbuf, char byte) +{ + cbuf->buf[cbuf_index(cbuf, cbuf->end)] = byte; + cbuf->end++; + cbuf_update_start(cbuf); +} + int cbuf_pop(struct cbuf *cbuf, char *bytep) { @@ -67,13 +73,76 @@ cbuf_pop(struct cbuf *cbuf, char *bytep) } int -cbuf_read(const struct cbuf *cbuf, size_t index, char *bytep) +cbuf_write(struct cbuf *cbuf, size_t index, const char *buf, size_t size) { - /* Mind integer overflows */ - if ((cbuf->end - index - 1) >= cbuf_size(cbuf)) { + char *start, *end, *buf_end; + size_t new_end, skip; + + if ((cbuf->end - index) > cbuf_size(cbuf)) { + return ERROR_INVAL; + } + + new_end = index + size; + + if ((new_end - cbuf->start) > cbuf_size(cbuf)) { + cbuf->end = new_end; + + if (size > cbuf_capacity(cbuf)) { + skip = size - cbuf_capacity(cbuf); + buf += skip; + index += skip; + size = cbuf_capacity(cbuf); + } + } + + start = &cbuf->buf[cbuf_index(cbuf, index)]; + end = start + size; + buf_end = cbuf->buf + cbuf->capacity; + + if (end > buf_end) { + skip = buf_end - start; + memcpy(start, buf, skip); + buf += skip; + start = cbuf->buf; + size -= skip; + } + + memcpy(start, buf, size); + cbuf_update_start(cbuf); + return 0; +} + +int +cbuf_read(const struct cbuf *cbuf, size_t index, char *buf, size_t *sizep) +{ + const char *start, *end, *buf_end; + size_t size; + + size = cbuf->end - index; + + /* At least one byte must be available */ + if ((size - 1) >= cbuf_size(cbuf)) { return ERROR_INVAL; } - *bytep = cbuf->buf[cbuf_index(cbuf, index)]; + if (*sizep > size) { + *sizep = size; + } + + start = &cbuf->buf[cbuf_index(cbuf, index)]; + end = start + *sizep; + buf_end = cbuf->buf + cbuf->capacity; + + if (end <= buf_end) { + size = *sizep; + } else { + size = buf_end - start; + memcpy(buf, start, size); + buf += size; + start = cbuf->buf; + size = *sizep - size; + } + + memcpy(buf, start, size); return 0; } diff --git a/kern/cbuf.h b/kern/cbuf.h index b675fd5..0cac54a 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -92,12 +92,25 @@ void cbuf_push(struct cbuf *cbuf, char byte); int cbuf_pop(struct cbuf *cbuf, char *bytep); /* - * Read a byte at a specific location. + * Write into a circular buffer at a specific location. * * If the given index is outside buffer boundaries, ERROR_INVAL is returned. - * Otherwise the byte is stored at the bytep address and 0 is returned. - * The buffer isn't changed by this operation. + * Otherwise size bytes are copied into the circular buffer. If the range + * in the circular buffer goes beyond its end, the end index is updated as + * appropriate. If the buffer is full when extending its end, the oldest + * bytes are overwritten and the start index is updated accordingly. */ -int cbuf_read(const struct cbuf *cbuf, size_t index, char *bytep); +int cbuf_write(struct cbuf *cbuf, size_t index, const char *buf, size_t size); + +/* + * Read from a circular buffer at a specific location. + * + * If the given index is outside buffer boundaries, ERROR_INVAL is returned. + * Otherwise at most *sizep bytes are copied into the given character buffer, + * and *sizep is updated to the number of bytes actually copied. + * + * The circular buffer isn't changed by this operation. + */ +int cbuf_read(const struct cbuf *cbuf, size_t index, char *buf, size_t *sizep); #endif /* _KERN_CBUF_H */ diff --git a/kern/log.c b/kern/log.c index a3b34f3..9f95ca8 100644 --- a/kern/log.c +++ b/kern/log.c @@ -144,8 +144,8 @@ log_record_consume(struct log_record *record, char c, size_t *sizep) static int log_record_init_consume(struct log_record *record, unsigned long *indexp) { + size_t size, c_size; bool marker_found; - size_t size; int error; char c; @@ -161,7 +161,8 @@ log_record_init_consume(struct log_record *record, unsigned long *indexp) break; } - error = cbuf_read(&log_cbuf, *indexp, &c); + c_size = 1; + error = cbuf_read(&log_cbuf, *indexp, &c, &c_size); if (error) { *indexp = cbuf_start(&log_cbuf); -- cgit v1.2.3 From e65bf4657346f5ee28fc0e41380d98c80c70cc65 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 17 Jun 2017 21:07:01 +0200 Subject: kern/log: use the new buffered cbuf interface The new code also fixes mistakes introduced by commit 164ccc1eeef9b335efa48ff3985a5c1c35bdecad, which replaced calls to cbuf_pop with cbuf_read, which doesn't update the start index, so that the shell command would know where to start dumping from. Some conditions were still relaying on the start index reflecting consumption. --- kern/log.c | 142 +++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 37 deletions(-) diff --git a/kern/log.c b/kern/log.c index 9f95ca8..135d402 100644 --- a/kern/log.c +++ b/kern/log.c @@ -47,7 +47,13 @@ static struct thread *log_thread; static struct cbuf log_cbuf; static char log_buffer[LOG_BUFFER_SIZE]; -static unsigned long log_nr_overruns; +/* + * This index is used by the log thread to report what it has consumed + * so that producers can detect overruns. + */ +static size_t log_index; + +static size_t log_nr_overruns; static unsigned int log_print_level; @@ -70,14 +76,66 @@ struct log_record { char buffer[LOG_MSG_SIZE]; }; +/* + * A consumer context allows faster, buffered reads of the circular buffer, + * by retaining bytes that weren't consumed in case a read spans multiple + * records. + */ +struct log_consume_ctx { + struct cbuf *cbuf; + size_t cbuf_index; + char buf[LOG_MSG_SIZE]; + size_t index; + size_t size; +}; + static void -log_push(char c) +log_consume_ctx_init(struct log_consume_ctx *ctx, struct cbuf *cbuf) { - if (cbuf_size(&log_cbuf) == cbuf_capacity(&log_cbuf)) { - log_nr_overruns++; + ctx->cbuf = cbuf; + ctx->cbuf_index = cbuf_start(cbuf); + ctx->index = 0; + ctx->size = 0; +} + +static size_t +log_consume_ctx_index(const struct log_consume_ctx *ctx) +{ + return ctx->cbuf_index; +} + +static void +log_consume_ctx_set_index(struct log_consume_ctx *ctx, size_t index) +{ + ctx->cbuf_index = index; +} + +static bool +log_consume_ctx_empty(const struct log_consume_ctx *ctx) +{ + return ctx->cbuf_index == cbuf_end(ctx->cbuf); +} + +static int +log_consume_ctx_pop(struct log_consume_ctx *ctx, char *byte) +{ + int error; + + if (ctx->index >= ctx->size) { + ctx->index = 0; + ctx->size = sizeof(ctx->buf); + error = cbuf_read(ctx->cbuf, ctx->cbuf_index, ctx->buf, &ctx->size); + + if (error) { + ctx->cbuf_index = cbuf_start(ctx->cbuf); + return error; + } } - cbuf_push(&log_cbuf, c); + ctx->cbuf_index++; + *byte = ctx->buf[ctx->index]; + ctx->index++; + return 0; } static const char * @@ -142,10 +200,10 @@ log_record_consume(struct log_record *record, char c, size_t *sizep) } static int -log_record_init_consume(struct log_record *record, unsigned long *indexp) +log_record_init_consume(struct log_record *record, struct log_consume_ctx *ctx) { - size_t size, c_size; bool marker_found; + size_t size; int error; char c; @@ -153,7 +211,7 @@ log_record_init_consume(struct log_record *record, unsigned long *indexp) size = 0; for (;;) { - if (*indexp == cbuf_end(&log_cbuf)) { + if (log_consume_ctx_empty(ctx)) { if (!marker_found) { return ERROR_INVAL; } @@ -161,16 +219,12 @@ log_record_init_consume(struct log_record *record, unsigned long *indexp) break; } - c_size = 1; - error = cbuf_read(&log_cbuf, *indexp, &c, &c_size); + error = log_consume_ctx_pop(ctx, &c); if (error) { - *indexp = cbuf_start(&log_cbuf); continue; } - (*indexp)++; - if (!marker_found) { if (c != LOG_MARKER) { continue; @@ -209,25 +263,11 @@ log_record_print(const struct log_record *record, unsigned int level) } } -static void -log_record_push(struct log_record *record) -{ - const char *ptr; - - ptr = (const char *)record; - - while (*ptr != '\0') { - log_push(*ptr); - ptr++; - } - - log_push('\0'); -} - static void log_run(void *arg) { - unsigned long flags, index, nr_overruns; + unsigned long flags, nr_overruns; + struct log_consume_ctx ctx; struct log_record record; bool start_shell; int error; @@ -239,10 +279,10 @@ log_run(void *arg) spinlock_lock_intr_save(&log_lock, &flags); - index = cbuf_start(&log_cbuf); + log_consume_ctx_init(&ctx, &log_cbuf); for (;;) { - while (index == cbuf_end(&log_cbuf)) { + while (log_consume_ctx_empty(&ctx)) { /* * Starting the shell after the log thread sleeps for the first * time cleanly serializes log messages and shell prompt, making @@ -253,17 +293,23 @@ log_run(void *arg) start_shell = false; } + log_index = log_consume_ctx_index(&ctx); + thread_sleep(&log_lock, &log_cbuf, "log_cbuf"); + + log_consume_ctx_set_index(&ctx, log_index); } - error = log_record_init_consume(&record, &index); + error = log_record_init_consume(&record, &ctx); /* Drain the log buffer before reporting overruns */ - if (cbuf_size(&log_cbuf) == 0) { + if (log_consume_ctx_empty(&ctx)) { nr_overruns = log_nr_overruns; log_nr_overruns = 0; } + log_index = log_consume_ctx_index(&ctx); + spinlock_unlock_intr_restore(&log_lock, flags); if (!error) { @@ -277,6 +323,8 @@ log_run(void *arg) } spinlock_lock_intr_save(&log_lock, &flags); + + log_consume_ctx_set_index(&ctx, log_index); } } @@ -285,16 +333,17 @@ log_run(void *arg) static void log_dump(unsigned int level) { - unsigned long index, flags; + struct log_consume_ctx ctx; struct log_record record; + unsigned long flags; int error; spinlock_lock_intr_save(&log_lock, &flags); - index = cbuf_start(&log_cbuf); + log_consume_ctx_init(&ctx, &log_cbuf); for (;;) { - error = log_record_init_consume(&record, &index); + error = log_record_init_consume(&record, &ctx); if (error) { break; @@ -352,6 +401,7 @@ void __init log_setup(void) { cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); + log_index = cbuf_start(&log_cbuf); spinlock_init(&log_lock); log_print_level = LOG_INFO; } @@ -373,6 +423,20 @@ log_start(void) SHELL_REGISTER_CMDS(log_shell_cmds); } +static void +log_write(const void *s, size_t size) +{ + int error; + + error = cbuf_write(&log_cbuf, cbuf_end(&log_cbuf), s, size); + assert(!error); + + if ((log_index - cbuf_start(&log_cbuf)) > cbuf_end(&log_cbuf)) { + log_nr_overruns += cbuf_start(&log_cbuf) - log_index; + log_index = cbuf_start(&log_cbuf); + } +} + int log_msg(unsigned int level, const char *format, ...) { @@ -392,6 +456,7 @@ log_vmsg(unsigned int level, const char *format, va_list ap) struct log_record record; unsigned long flags; int nr_chars; + size_t size; log_record_init_produce(&record, level); nr_chars = vsnprintf(record.buffer, sizeof(record.buffer), format, ap); @@ -401,8 +466,11 @@ log_vmsg(unsigned int level, const char *format, va_list ap) goto out; } + assert(nr_chars >= 0); + size = offsetof(struct log_record, buffer) + nr_chars + 1; + spinlock_lock_intr_save(&log_lock, &flags); - log_record_push(&record); + log_write(&record, size); thread_wakeup(log_thread); spinlock_unlock_intr_restore(&log_lock, flags); -- cgit v1.2.3 From cf1c3901ead1182129c65c63b2035febecc36f64 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 13:46:15 +0200 Subject: kern/console: fix console_putc --- kern/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/console.c b/kern/console.c index bee400e..00d6e46 100644 --- a/kern/console.c +++ b/kern/console.c @@ -72,7 +72,7 @@ console_putc(struct console *console, char c) unsigned long flags; spinlock_lock_intr_save(&console->lock, &flags); - console->ops->putc(console_dev, c); + console->ops->putc(console, c); spinlock_unlock_intr_restore(&console->lock, flags); } -- cgit v1.2.3 From 106bb2e3f3e658fcb6af72414f73cc49b32f836d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 20:44:35 +0200 Subject: kern/cbuf: use void pointers with cbuf_read and cbuf_write --- kern/cbuf.c | 4 ++-- kern/cbuf.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index 83032e5..c38cc1d 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -73,7 +73,7 @@ cbuf_pop(struct cbuf *cbuf, char *bytep) } int -cbuf_write(struct cbuf *cbuf, size_t index, const char *buf, size_t size) +cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size) { char *start, *end, *buf_end; size_t new_end, skip; @@ -113,7 +113,7 @@ cbuf_write(struct cbuf *cbuf, size_t index, const char *buf, size_t size) } int -cbuf_read(const struct cbuf *cbuf, size_t index, char *buf, size_t *sizep) +cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep) { const char *start, *end, *buf_end; size_t size; diff --git a/kern/cbuf.h b/kern/cbuf.h index 0cac54a..3566ac4 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -100,17 +100,17 @@ int cbuf_pop(struct cbuf *cbuf, char *bytep); * appropriate. If the buffer is full when extending its end, the oldest * bytes are overwritten and the start index is updated accordingly. */ -int cbuf_write(struct cbuf *cbuf, size_t index, const char *buf, size_t size); +int cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size); /* * Read from a circular buffer at a specific location. * * If the given index is outside buffer boundaries, ERROR_INVAL is returned. - * Otherwise at most *sizep bytes are copied into the given character buffer, + * Otherwise at most *sizep bytes are copied into the given byte buffer, * and *sizep is updated to the number of bytes actually copied. * * The circular buffer isn't changed by this operation. */ -int cbuf_read(const struct cbuf *cbuf, size_t index, char *buf, size_t *sizep); +int cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep); #endif /* _KERN_CBUF_H */ -- cgit v1.2.3 From 6fda4ae759f96519037e71ffd3c8b298d388ff0c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 20:44:58 +0200 Subject: kern/console: add support for scrolling --- kern/console.c | 21 ++++++++++++++++++++- kern/console.h | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/kern/console.c b/kern/console.c index 00d6e46..e026ffd 100644 --- a/kern/console.c +++ b/kern/console.c @@ -66,6 +66,21 @@ console_init(struct console *console, const char *name, strlcpy(console->name, name, sizeof(console->name)); } +static int +console_process_ctrl_char(struct console *console, char c) +{ + switch (c) { + case CONSOLE_SCROLL_UP: + case CONSOLE_SCROLL_DOWN: + break; + default: + return ERROR_INVAL; + } + + console->ops->putc(console, c); + return 0; +} + static void console_putc(struct console *console, char c) { @@ -96,7 +111,11 @@ console_getc(struct console *console) error = cbuf_pop(&console->recvbuf, &c); if (!error) { - break; + error = console_process_ctrl_char(console, c); + + if (error) { + break; + } } thread_sleep(&console->lock, console, "consgetc"); diff --git a/kern/console.h b/kern/console.h index 7455a05..a5aa478 100644 --- a/kern/console.h +++ b/kern/console.h @@ -26,6 +26,9 @@ #include #include +#define CONSOLE_SCROLL_UP 0x12 /* DC2 */ +#define CONSOLE_SCROLL_DOWN 0x14 /* DC4 */ + struct console; struct console_ops { -- cgit v1.2.3 From a44ae4206bbdcd6c00a990389fa2ccf32386f384 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 20:45:31 +0200 Subject: x86/cga: implement scrolling --- arch/x86/machine/cga.c | 354 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 308 insertions(+), 46 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 1d1cc4b..16aab40 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Richard Braun. + * Copyright (c) 2010-2017 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 @@ -15,10 +15,15 @@ * along with this program. If not, see . */ +#include +#include #include #include +#include #include +#include +#include #include #include #include @@ -34,8 +39,9 @@ /* * Text mode mapped memory and size. */ -#define CGA_MEMORY 0xb8000 -#define CGA_MEMORY_SIZE (CGA_COLUMNS * CGA_LINES * 2) +#define CGA_MEMORY 0xb8000 +#define CGA_MEMORY_LINE_SIZE (CGA_COLUMNS * 2) +#define CGA_MEMORY_SIZE (CGA_MEMORY_LINE_SIZE * CGA_LINES) /* * I/O ports. @@ -48,26 +54,56 @@ /* * CRTC registers. */ +#define CGA_CRTC_CURSOR_START_REGISTER 0xa #define CGA_CRTC_CURSOR_LOCATION_HIGH_REGISTER 0xe #define CGA_CRTC_CURSOR_LOCATION_LOW_REGISTER 0xf /* - * Foreground screen color. + * Cursor start register bits. */ -#define CGA_FOREGROUND_COLOR 0x7 +#define CGA_CSR_DISABLED 0x10 /* - * Blank space 16 bits word. + * Foreground screen color. */ -#define CGA_BLANK ((CGA_FOREGROUND_COLOR << 8) | ' ') +#define CGA_FOREGROUND_COLOR 0x7 /* * Number of spaces to display for a tabulation. */ #define CGA_TABULATION_SPACES 8 -static uint8_t *cga_memory __read_mostly; -static uint16_t cga_cursor; +static void *cga_memory __read_mostly; + +#define CGA_BACK_BUFFER_SIZE (64 * 1024) + +#define CGA_SCROLL_PAGE ((CGA_LINES / 2) * CGA_MEMORY_LINE_SIZE) + +/* + * Back buffer. + */ +struct cga_bbuf { + struct cbuf cbuf; + size_t view; + size_t cursor; + char buf[CGA_BACK_BUFFER_SIZE]; + bool cursor_enabled; +}; + +static struct cga_bbuf cga_bbuf; + +static uint16_t +cga_build_cell(char c) +{ + return (CGA_FOREGROUND_COLOR << 8) | c; +} + +static void +cga_write(size_t index, const void *ptr, size_t size) +{ + assert((index + size) <= CGA_MEMORY_SIZE); + memcpy(cga_memory + index, ptr, size); +} static uint16_t cga_get_cursor_position(void) @@ -95,25 +131,267 @@ cga_set_cursor_position(uint16_t position) io_write_byte(CGA_CRTC_DATA_REGISTER, position & 0xff); } -static uint8_t -cga_get_cursor_column(void) +static void +cga_enable_cursor(void) +{ + uint8_t tmp; + + io_write_byte(CGA_CRTC_ADDRESS_REGISTER, CGA_CRTC_CURSOR_START_REGISTER); + tmp = io_read_byte(CGA_CRTC_DATA_REGISTER); + io_write_byte(CGA_CRTC_DATA_REGISTER, tmp & ~CGA_CSR_DISABLED); +} + +static void +cga_disable_cursor(void) +{ + uint8_t tmp; + + io_write_byte(CGA_CRTC_ADDRESS_REGISTER, CGA_CRTC_CURSOR_START_REGISTER); + tmp = io_read_byte(CGA_CRTC_DATA_REGISTER); + io_write_byte(CGA_CRTC_DATA_REGISTER, tmp | CGA_CSR_DISABLED); +} + +static void +cga_bbuf_init(struct cga_bbuf *bbuf, size_t cursor) +{ + cbuf_init(&bbuf->cbuf, bbuf->buf, sizeof(bbuf->buf)); + bbuf->view = cbuf_start(&bbuf->cbuf); + cbuf_write(&bbuf->cbuf, bbuf->view, cga_memory, CGA_MEMORY_SIZE); + bbuf->cursor = bbuf->view + cursor; + bbuf->cursor_enabled = true; +} + +static size_t +cga_bbuf_cursor_offset(const struct cga_bbuf *bbuf) +{ + return bbuf->cursor - bbuf->view; +} + +static int +cga_bbuf_get_phys_cursor(const struct cga_bbuf *bbuf, uint16_t *cursorp) +{ + uint16_t cursor; + + cursor = cga_bbuf_cursor_offset(bbuf); + assert((cursor & 1) == 0); + + if (cursor >= CGA_MEMORY_SIZE) { + return ERROR_NODEV; + } + + *cursorp = (cursor >> 1); + return 0; +} + +static void +cga_bbuf_update_phys_cursor(struct cga_bbuf *bbuf) +{ + bool cursor_enabled; + uint16_t cursor; + int error; + + error = cga_bbuf_get_phys_cursor(bbuf, &cursor); + cursor_enabled = !error; + + if (cursor_enabled != bbuf->cursor_enabled) { + bbuf->cursor_enabled = cursor_enabled; + + if (cursor_enabled) { + cga_enable_cursor(); + } else { + cga_disable_cursor(); + } + } + + if (cursor_enabled) { + cga_set_cursor_position(cursor); + } +} + +static void +cga_bbuf_redraw(struct cga_bbuf *bbuf) +{ + size_t size; + int error; + + size = CGA_MEMORY_SIZE; + error = cbuf_read(&bbuf->cbuf, bbuf->view, cga_memory, &size); + assert(!error); + assert(size == CGA_MEMORY_SIZE); + cga_bbuf_update_phys_cursor(bbuf); +} + +static bool +cga_bbuf_view_needs_scrolling(const struct cga_bbuf *bbuf) +{ + size_t view_size; + + view_size = cbuf_end(&bbuf->cbuf) - bbuf->view; + + if (view_size > CGA_MEMORY_SIZE) { + return true; + } + + /* Consider the cursor as a valid cell */ + view_size = (bbuf->cursor + 1) - bbuf->view; + + if (view_size > CGA_MEMORY_SIZE) { + return true; + } + + return false; +} + +static void +cga_bbuf_scroll_once(struct cga_bbuf *bbuf) +{ + uint16_t spaces[CGA_COLUMNS]; + size_t i; + + for (i = 0; i < ARRAY_SIZE(spaces); i++) { + spaces[i] = cga_build_cell(' '); + } + + cbuf_write(&bbuf->cbuf, cbuf_end(&bbuf->cbuf), spaces, sizeof(spaces)); + bbuf->view += sizeof(spaces); + cga_bbuf_redraw(bbuf); +} + +static void +cga_bbuf_reset_view(struct cga_bbuf *bbuf) +{ + if ((bbuf->view + CGA_MEMORY_SIZE) == cbuf_end(&bbuf->cbuf)) { + return; + } + + bbuf->view = cbuf_end(&bbuf->cbuf) - CGA_MEMORY_SIZE; + cga_bbuf_redraw(bbuf); +} + +static void +cga_bbuf_push(struct cga_bbuf *bbuf, char c) +{ + size_t offset; + uint16_t cell; + + cga_bbuf_reset_view(bbuf); + + cell = cga_build_cell(c); + cbuf_write(&bbuf->cbuf, bbuf->cursor, &cell, sizeof(cell)); + offset = cga_bbuf_cursor_offset(bbuf); + bbuf->cursor += sizeof(cell); + + if (cga_bbuf_view_needs_scrolling(bbuf)) { + cga_bbuf_scroll_once(bbuf); + } else { + cga_write(offset, &cell, sizeof(cell)); + cga_bbuf_update_phys_cursor(bbuf); + } +} + +static void +cga_bbuf_newline(struct cga_bbuf *bbuf) { - return cga_cursor % CGA_COLUMNS; + uint16_t cursor, spaces[CGA_COLUMNS]; + size_t i, nr_spaces, offset; + int error; + + cga_bbuf_reset_view(bbuf); + + error = cga_bbuf_get_phys_cursor(bbuf, &cursor); + assert(!error); + + nr_spaces = CGA_COLUMNS - (cursor % CGA_COLUMNS); + + for (i = 0; i < nr_spaces; i++) { + spaces[i] = cga_build_cell(' '); + } + + cbuf_write(&bbuf->cbuf, bbuf->cursor, + spaces, nr_spaces * sizeof(spaces[0])); + offset = cga_bbuf_cursor_offset(bbuf); + bbuf->cursor += nr_spaces * sizeof(spaces[0]); + + if (cga_bbuf_view_needs_scrolling(bbuf)) { + cga_bbuf_scroll_once(bbuf); + } else { + cga_write(offset, spaces, nr_spaces * sizeof(spaces[0])); + cga_bbuf_update_phys_cursor(bbuf); + } + + cga_bbuf_update_phys_cursor(bbuf); } static void -cga_scroll_lines(void) +cga_bbuf_move_cursor(struct cga_bbuf *bbuf, bool forward) { - uint16_t *last_line; - int i; + cga_bbuf_reset_view(bbuf); - memmove(cga_memory, (uint16_t *)cga_memory + CGA_COLUMNS, - CGA_MEMORY_SIZE - (CGA_COLUMNS * 2)); - last_line = (uint16_t *)cga_memory + (CGA_COLUMNS * (CGA_LINES - 1)); + if ((!forward && (bbuf->cursor == bbuf->view)) + || (forward && (bbuf->cursor == cbuf_end(&bbuf->cbuf)))) { + return; + } - for(i = 0; i < CGA_COLUMNS; i++) { - last_line[i] = CGA_BLANK; + if (forward) { + bbuf->cursor += sizeof(uint16_t); + } else { + bbuf->cursor -= sizeof(uint16_t); } + + cga_bbuf_update_phys_cursor(bbuf); +} + +static void +cga_bbuf_move_cursor_left(struct cga_bbuf *bbuf) +{ + cga_bbuf_move_cursor(bbuf, false); +} + +static void +cga_bbuf_move_cursor_right(struct cga_bbuf *bbuf) +{ + cga_bbuf_move_cursor(bbuf, true); +} + +static void +cga_bbuf_backspace(struct cga_bbuf *bbuf) +{ + cga_bbuf_move_cursor_left(bbuf); +} + +static void +cga_bbuf_scroll_up(struct cga_bbuf *bbuf) +{ + size_t start, size; + + bbuf->view -= CGA_SCROLL_PAGE; + + /* The back buffer size is a power-of-two, not a line multiple */ + size = cbuf_size(&bbuf->cbuf); + size -= size % CGA_MEMORY_LINE_SIZE; + start = cbuf_end(&bbuf->cbuf) - size; + + if ((bbuf->view - start) >= size) { + bbuf->view = start; + } + + cga_bbuf_redraw(bbuf); +} + +static void +cga_bbuf_scroll_down(struct cga_bbuf *bbuf) +{ + size_t end; + + bbuf->view += CGA_SCROLL_PAGE; + + end = bbuf->view + CGA_MEMORY_SIZE - cbuf_start(&bbuf->cbuf); + + if (end > cbuf_size(&bbuf->cbuf)) { + bbuf->view = cbuf_end(&bbuf->cbuf) - CGA_MEMORY_SIZE; + } + + cga_bbuf_redraw(bbuf); } void @@ -122,35 +400,21 @@ cga_putc(char c) if (c == '\r') { return; } else if (c == '\n') { - cga_cursor += CGA_COLUMNS - cga_get_cursor_column(); - - if (cga_cursor >= (CGA_LINES * CGA_COLUMNS)) { - cga_scroll_lines(); - cga_cursor -= CGA_COLUMNS; - } - - cga_set_cursor_position(cga_cursor); + cga_bbuf_newline(&cga_bbuf); } else if (c == '\b') { - if (cga_cursor > 0) { - cga_cursor--; - cga_set_cursor_position(cga_cursor); - } + cga_bbuf_backspace(&cga_bbuf); } else if (c == '\t') { int i; for(i = 0; i < CGA_TABULATION_SPACES; i++) { cga_putc(' '); } + } else if (c == CONSOLE_SCROLL_UP) { + cga_bbuf_scroll_up(&cga_bbuf); + } else if (c == CONSOLE_SCROLL_DOWN) { + cga_bbuf_scroll_down(&cga_bbuf); } else { - if ((cga_cursor + 1) >= CGA_COLUMNS * CGA_LINES) { - cga_scroll_lines(); - cga_cursor -= CGA_COLUMNS; - } - - ((uint16_t *)cga_memory)[cga_cursor] = ((CGA_FOREGROUND_COLOR << 8) - | c); - cga_cursor++; - cga_set_cursor_position(cga_cursor); + cga_bbuf_push(&cga_bbuf, c); } } @@ -178,19 +442,17 @@ cga_setup(void) io_write_byte(CGA_MISC_OUTPUT_REGISTER_WRITE, misc_output_register); } - cga_cursor = cga_get_cursor_position(); + cga_bbuf_init(&cga_bbuf, cga_get_cursor_position()); } void cga_cursor_left(void) { - cga_cursor--; - cga_set_cursor_position(cga_cursor); + cga_bbuf_move_cursor_left(&cga_bbuf); } void cga_cursor_right(void) { - cga_cursor++; - cga_set_cursor_position(cga_cursor); + cga_bbuf_move_cursor_right(&cga_bbuf); } -- cgit v1.2.3 From c3c8291754aabc7279bcf4d08bea6d3e395e0015 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 20:46:02 +0200 Subject: x86/atkbd: implement scrolling key combinations --- arch/x86/machine/atkbd.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index da792bd..b23772e 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -176,6 +177,9 @@ enum atkbd_key_id { ATKBD_KEY_KP_DEL, }; +static const char atkbd_scroll_up[] = { CONSOLE_SCROLL_UP, '\0' }; +static const char atkbd_scroll_down[] = { CONSOLE_SCROLL_DOWN, '\0' }; + /* * Key modifiers. */ @@ -288,8 +292,8 @@ static const struct atkbd_key atkbd_e0_keys[] = { [0x71] = { 0, ATKBD_KEY_DELETE }, [0x6c] = { 0, ATKBD_KEY_HOME }, [0x69] = { 0, ATKBD_KEY_END }, - [0x7d] = { 0, ATKBD_KEY_PGUP }, - [0x7a] = { 0, ATKBD_KEY_PGDOWN }, + [0x7d] = { ATKBD_KM_SHIFT, ATKBD_KEY_PGUP }, + [0x7a] = { ATKBD_KM_SHIFT, ATKBD_KEY_PGDOWN }, [0x6b] = { ATKBD_KM_CTL, ATKBD_KEY_LEFT }, [0x72] = { ATKBD_KM_CTL, ATKBD_KEY_BOTTOM }, @@ -426,6 +430,9 @@ static const char *atkbd_shift_chars[] = { [ATKBD_KEY_COMMA] = "<", [ATKBD_KEY_DOT] = ">", [ATKBD_KEY_SLASH] = "?", + + [ATKBD_KEY_PGUP] = atkbd_scroll_up, + [ATKBD_KEY_PGDOWN] = atkbd_scroll_down, }; /* -- cgit v1.2.3 From 5c9ebff975b689554e0a504a9cb629aa29c90b0d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 18 Jun 2017 22:23:12 +0200 Subject: x86/cga: silence compiler warnings The warning, which is about a variable that may be used uninitialized, is a false positive. --- arch/x86/machine/cga.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 16aab40..8e03b50 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -187,7 +187,7 @@ static void cga_bbuf_update_phys_cursor(struct cga_bbuf *bbuf) { bool cursor_enabled; - uint16_t cursor; + uint16_t cursor = 0; int error; error = cga_bbuf_get_phys_cursor(bbuf, &cursor); @@ -292,7 +292,7 @@ cga_bbuf_push(struct cga_bbuf *bbuf, char c) static void cga_bbuf_newline(struct cga_bbuf *bbuf) { - uint16_t cursor, spaces[CGA_COLUMNS]; + uint16_t cursor = 0, spaces[CGA_COLUMNS]; size_t i, nr_spaces, offset; int error; -- cgit v1.2.3 From 1b4b026ca7a63d52c4a33ed3212786aa03aa1b1f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 19 Jun 2017 21:10:56 +0200 Subject: x86/cga: minor refactoring --- arch/x86/machine/cga.c | 111 ++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 8e03b50..e364b91 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -46,17 +46,22 @@ /* * I/O ports. */ -#define CGA_MISC_OUTPUT_REGISTER_READ 0x3cc -#define CGA_MISC_OUTPUT_REGISTER_WRITE 0x3c2 -#define CGA_CRTC_ADDRESS_REGISTER 0x3d4 -#define CGA_CRTC_DATA_REGISTER 0x3d5 +#define CGA_PORT_MISC_OUT_READ 0x3cc +#define CGA_PORT_MISC_OUT_WRITE 0x3c2 +#define CGA_PORT_CRTC_ADDR 0x3d4 +#define CGA_PORT_CRTC_DATA 0x3d5 + +/* + * Miscellaneous output register bits. + */ +#define CGA_MISC_OUT_IOAS 0x1 /* * CRTC registers. */ -#define CGA_CRTC_CURSOR_START_REGISTER 0xa -#define CGA_CRTC_CURSOR_LOCATION_HIGH_REGISTER 0xe -#define CGA_CRTC_CURSOR_LOCATION_LOW_REGISTER 0xf +#define CGA_CRTC_CURSOR_START_REG 0xa +#define CGA_CRTC_CURSOR_LOC_HIGH_REG 0xe +#define CGA_CRTC_CURSOR_LOC_LOW_REG 0xf /* * Cursor start register bits. @@ -110,12 +115,12 @@ cga_get_cursor_position(void) { uint16_t tmp; - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, - CGA_CRTC_CURSOR_LOCATION_HIGH_REGISTER); - tmp = io_read_byte(CGA_CRTC_DATA_REGISTER) << 8; - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, - CGA_CRTC_CURSOR_LOCATION_LOW_REGISTER); - tmp |= io_read_byte(CGA_CRTC_DATA_REGISTER); + io_write_byte(CGA_PORT_CRTC_ADDR, + CGA_CRTC_CURSOR_LOC_HIGH_REG); + tmp = io_read_byte(CGA_PORT_CRTC_DATA) << 8; + io_write_byte(CGA_PORT_CRTC_ADDR, + CGA_CRTC_CURSOR_LOC_LOW_REG); + tmp |= io_read_byte(CGA_PORT_CRTC_DATA); return tmp; } @@ -123,12 +128,10 @@ cga_get_cursor_position(void) static void cga_set_cursor_position(uint16_t position) { - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, - CGA_CRTC_CURSOR_LOCATION_HIGH_REGISTER); - io_write_byte(CGA_CRTC_DATA_REGISTER, position >> 8); - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, - CGA_CRTC_CURSOR_LOCATION_LOW_REGISTER); - io_write_byte(CGA_CRTC_DATA_REGISTER, position & 0xff); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_LOC_HIGH_REG); + io_write_byte(CGA_PORT_CRTC_DATA, position >> 8); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_LOC_LOW_REG); + io_write_byte(CGA_PORT_CRTC_DATA, position & 0xff); } static void @@ -136,9 +139,9 @@ cga_enable_cursor(void) { uint8_t tmp; - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, CGA_CRTC_CURSOR_START_REGISTER); - tmp = io_read_byte(CGA_CRTC_DATA_REGISTER); - io_write_byte(CGA_CRTC_DATA_REGISTER, tmp & ~CGA_CSR_DISABLED); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_START_REG); + tmp = io_read_byte(CGA_PORT_CRTC_DATA); + io_write_byte(CGA_PORT_CRTC_DATA, tmp & ~CGA_CSR_DISABLED); } static void @@ -146,9 +149,9 @@ cga_disable_cursor(void) { uint8_t tmp; - io_write_byte(CGA_CRTC_ADDRESS_REGISTER, CGA_CRTC_CURSOR_START_REGISTER); - tmp = io_read_byte(CGA_CRTC_DATA_REGISTER); - io_write_byte(CGA_CRTC_DATA_REGISTER, tmp | CGA_CSR_DISABLED); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_START_REG); + tmp = io_read_byte(CGA_PORT_CRTC_DATA); + io_write_byte(CGA_PORT_CRTC_DATA, tmp | CGA_CSR_DISABLED); } static void @@ -397,51 +400,53 @@ cga_bbuf_scroll_down(struct cga_bbuf *bbuf) void cga_putc(char c) { - if (c == '\r') { + unsigned int i; + + switch (c) { + case '\r': return; - } else if (c == '\n') { + case '\n': cga_bbuf_newline(&cga_bbuf); - } else if (c == '\b') { + break; + case '\b': cga_bbuf_backspace(&cga_bbuf); - } else if (c == '\t') { - int i; - + break; + case '\t': for(i = 0; i < CGA_TABULATION_SPACES; i++) { cga_putc(' '); } - } else if (c == CONSOLE_SCROLL_UP) { + + break; + case CONSOLE_SCROLL_UP: cga_bbuf_scroll_up(&cga_bbuf); - } else if (c == CONSOLE_SCROLL_DOWN) { + break; + case CONSOLE_SCROLL_DOWN: cga_bbuf_scroll_down(&cga_bbuf); - } else { + break; + default: cga_bbuf_push(&cga_bbuf, c); } } -void __init -cga_setup(void) +static void __init +cga_setup_misc_out(void) { - uint8_t misc_output_register; + uint8_t reg; - cga_memory = (void *)vm_page_direct_va(CGA_MEMORY); + reg = io_read_byte(CGA_PORT_MISC_OUT_READ); - /* - * Check if the Input/Output Address Select bit is set. - */ - misc_output_register = io_read_byte(CGA_MISC_OUTPUT_REGISTER_READ); - - if (!(misc_output_register & 0x1)) { - /* - * Set the I/O AS bit. - */ - misc_output_register |= 0x1; - - /* - * Update the misc output register. - */ - io_write_byte(CGA_MISC_OUTPUT_REGISTER_WRITE, misc_output_register); + if (!(reg & CGA_MISC_OUT_IOAS)) { + reg |= CGA_MISC_OUT_IOAS; + io_write_byte(CGA_PORT_MISC_OUT_WRITE, reg); } +} + +void __init +cga_setup(void) +{ + cga_memory = (void *)vm_page_direct_va(CGA_MEMORY); + cga_setup_misc_out(); cga_bbuf_init(&cga_bbuf, cga_get_cursor_position()); } -- cgit v1.2.3 From 83c826a255628cbb7371b17ead18ecc06ad22cd2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 19 Jun 2017 21:14:12 +0200 Subject: kern/cbuf: update copyright year --- kern/cbuf.c | 2 +- kern/cbuf.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index c38cc1d..cd8ee87 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Richard Braun. + * Copyright (c) 2015-2017 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 diff --git a/kern/cbuf.h b/kern/cbuf.h index 3566ac4..0558cc0 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Richard Braun. + * Copyright (c) 2015-2017 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 -- cgit v1.2.3 From b8db240bbee58034fcf44d31f14bf7ea951fdf64 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 19 Jun 2017 22:53:09 +0200 Subject: x86/atkbd: fix handling of unknown sequences --- arch/x86/machine/atkbd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index b23772e..bee0195 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -760,7 +760,7 @@ static void atkbd_key_process(const struct atkbd_key *key) { if (key->id == ATKBD_KEY_INVALID) { - return; + goto out; } if ((key->modifiers & ATKBD_KM_SHIFT) && (atkbd_flags & ATKBD_KF_SHIFT)) { @@ -774,6 +774,7 @@ atkbd_key_process(const struct atkbd_key *key) atkbd_key_process_chars(key, atkbd_chars, ARRAY_SIZE(atkbd_chars)); } +out: atkbd_flags &= ~ATKBD_KF_F0; } @@ -786,10 +787,12 @@ atkbd_process_e0_code(uint8_t code) } if (code >= ARRAY_SIZE(atkbd_e0_keys)) { - return; + goto out; } atkbd_key_process(&atkbd_e0_keys[code]); + +out: atkbd_flags &= ~ATKBD_KF_E0; } -- cgit v1.2.3 From bc4fdb1ff703877dec5413ab4ee3e79c770ff223 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 19 Jun 2017 23:01:24 +0200 Subject: doc/intro(9): mention the kern/cbuf module --- doc/intro.9.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 57a5c58..73a2198 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -111,6 +111,8 @@ other development tools : module:kern/bitmap:: Arbitrary-length bit array. +module:kern/cbuf:: + Circular character buffer. module:kern/error:: Common errors and error handling functions. module:kern/kmem:: @@ -132,8 +134,9 @@ module:kern/syscnt:: X15 doesn't provide a generic queue interface, because the requirements often vary too much. Similarly, it doesn't provide a hash table interface. -Instead, users can easily build specialized queues and hash tables on top -of the provided facilities. Hash functions may be provided in the future. +Instead, users can easily build specialized queues, hash tables and ring +buffers on top of the provided facilities. Hash functions may be provided +in the future. [[multiprocessor_support]] MULTIPROCESSOR SUPPORT -- cgit v1.2.3 From 4ec81c66c2339d31ae5dc3d628753e0d216862d8 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 19 Jun 2017 23:06:37 +0200 Subject: Reword the purpose of the project to insist on real-time --- README | 2 +- doc/intro.9.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 72ee131..e1daf2a 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ X15 is an open source microkernel. Its purpose is to provide a foundation -for a Hurd-like operating system or an embedded kernel application. +for a Hurd-like operating system as well as real-time kernel applications. See https://www.sceen.net/x15/. diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 73a2198..37691a1 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -13,7 +13,7 @@ DESCRIPTION ----------- X15 is an open source microkernel. Its purpose is to provide a foundation -for a Hurd-like operating system or an embedded kernel application. +for a Hurd-like operating system as well as real-time kernel applications. Section 9 of the manual describes kernel interfaces, both internal and provided to application code hosted in kernel mode. -- cgit v1.2.3 From be5eb1da462a2d715172a98aa236d4d88dbd557c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 22 Jun 2017 20:44:15 +0200 Subject: kern/cbuf: new cbuf_range_valid function --- kern/cbuf.c | 10 +++++----- kern/cbuf.h | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index cd8ee87..f00532e 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -78,13 +78,13 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size) char *start, *end, *buf_end; size_t new_end, skip; - if ((cbuf->end - index) > cbuf_size(cbuf)) { + if (!cbuf_range_valid(cbuf, index, cbuf->end)) { return ERROR_INVAL; } new_end = index + size; - if ((new_end - cbuf->start) > cbuf_size(cbuf)) { + if (!cbuf_range_valid(cbuf, cbuf->start, new_end)) { cbuf->end = new_end; if (size > cbuf_capacity(cbuf)) { @@ -118,13 +118,13 @@ cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep) const char *start, *end, *buf_end; size_t size; - size = cbuf->end - index; - /* At least one byte must be available */ - if ((size - 1) >= cbuf_size(cbuf)) { + if (!cbuf_range_valid(cbuf, index, index + 1)) { return ERROR_INVAL; } + size = cbuf->end - index; + if (*sizep > size) { *sizep = size; } diff --git a/kern/cbuf.h b/kern/cbuf.h index 0558cc0..a30e5f2 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -21,6 +21,7 @@ #ifndef _KERN_CBUF_H #define _KERN_CBUF_H +#include #include /* @@ -66,6 +67,12 @@ cbuf_clear(struct cbuf *cbuf) cbuf->start = cbuf->end; } +static inline bool +cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end) +{ + return ((end - start) <= cbuf_size(cbuf)); +} + /* * Initialize a circular buffer. * -- cgit v1.2.3 From 349440892725cc5aefd7839bc4d30611c0092898 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 22 Jun 2017 20:45:52 +0200 Subject: kern/cbuf: fix very unlikely but potential integer overflows --- kern/cbuf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index f00532e..ea848c5 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -99,7 +99,7 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size) end = start + size; buf_end = cbuf->buf + cbuf->capacity; - if (end > buf_end) { + if ((end <= cbuf->buf) || (end > buf_end)) { skip = buf_end - start; memcpy(start, buf, skip); buf += skip; @@ -133,7 +133,7 @@ cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep) end = start + *sizep; buf_end = cbuf->buf + cbuf->capacity; - if (end <= buf_end) { + if ((end > cbuf->buf) && (end <= buf_end)) { size = *sizep; } else { size = buf_end - start; -- cgit v1.2.3 From 71b302e2c41f7bfb92beda8961a6d57bef2bd14e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 22 Jun 2017 22:05:34 +0200 Subject: kern/string: fetch arch-specific defines from machine/string.h --- arch/x86/Makefrag.am | 1 + arch/x86/machine/param.h | 11 ----------- arch/x86/machine/string.c | 30 +++++++++++++++--------------- arch/x86/machine/string.h | 32 ++++++++++++++++++++++++++++++++ kern/string.c | 38 +++++++++++++++++++------------------- 5 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 arch/x86/machine/string.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index d4f9fe7..965bd0b 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -62,6 +62,7 @@ x15_SOURCES += \ arch/x86/machine/strace.c \ arch/x86/machine/strace.h \ arch/x86/machine/string.c \ + arch/x86/machine/string.h \ arch/x86/machine/tcb_asm.S \ arch/x86/machine/tcb.c \ arch/x86/machine/tcb.h \ diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index 6ee11cc..557bc80 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -51,17 +51,6 @@ */ #define __read_mostly __section(".data.read_mostly") -/* - * Provide architecture-specific string functions. - */ -#define ARCH_STRING_MEMCPY -#define ARCH_STRING_MEMMOVE -#define ARCH_STRING_MEMSET -#define ARCH_STRING_MEMCMP -#define ARCH_STRING_STRLEN -#define ARCH_STRING_STRCPY -#define ARCH_STRING_STRCMP - /* * System timer frequency. * diff --git a/arch/x86/machine/string.c b/arch/x86/machine/string.c index 10d9ee0..1f1cab4 100644 --- a/arch/x86/machine/string.c +++ b/arch/x86/machine/string.c @@ -18,9 +18,9 @@ #include #include -#include +#include -#ifdef ARCH_STRING_MEMCPY +#ifdef STRING_ARCH_MEMCPY void * memcpy(void *dest, const void *src, size_t n) { @@ -32,9 +32,9 @@ memcpy(void *dest, const void *src, size_t n) : : "memory"); return orig_dest; } -#endif /* ARCH_STRING_MEMCPY */ +#endif /* STRING_ARCH_MEMCPY */ -#ifdef ARCH_STRING_MEMMOVE +#ifdef STRING_ARCH_MEMMOVE void * memmove(void *dest, const void *src, size_t n) { @@ -56,9 +56,9 @@ memmove(void *dest, const void *src, size_t n) return orig_dest; } -#endif /* ARCH_STRING_MEMMOVE */ +#endif /* STRING_ARCH_MEMMOVE */ -#ifdef ARCH_STRING_MEMSET +#ifdef STRING_ARCH_MEMSET void * memset(void *s, int c, size_t n) { @@ -71,9 +71,9 @@ memset(void *s, int c, size_t n) : "memory"); return orig_s; } -#endif /* ARCH_STRING_MEMSET */ +#endif /* STRING_ARCH_MEMSET */ -#ifdef ARCH_STRING_MEMCMP +#ifdef STRING_ARCH_MEMCMP int memcmp(const void *s1, const void *s2, size_t n) { @@ -90,9 +90,9 @@ memcmp(const void *s1, const void *s2, size_t n) c2 = *(((const unsigned char *)s2) - 1); return (int)c1 - (int)c2; } -#endif /* ARCH_STRING_MEMCMP */ +#endif /* STRING_ARCH_MEMCMP */ -#ifdef ARCH_STRING_STRLEN +#ifdef STRING_ARCH_STRLEN size_t strlen(const char *s) { @@ -105,9 +105,9 @@ strlen(const char *s) : "memory"); return ~n - 1; } -#endif /* ARCH_STRING_STRLEN */ +#endif /* STRING_ARCH_STRLEN */ -#ifdef ARCH_STRING_STRCPY +#ifdef STRING_ARCH_STRCPY char * strcpy(char *dest, const char *src) { @@ -123,9 +123,9 @@ strcpy(char *dest, const char *src) : : "al", "memory"); return orig_dest; } -#endif /* ARCH_STRING_STRCPY */ +#endif /* STRING_ARCH_STRCPY */ -#ifdef ARCH_STRING_STRCMP +#ifdef STRING_ARCH_STRCMP int strcmp(const char *s1, const char *s2) { @@ -144,4 +144,4 @@ strcmp(const char *s1, const char *s2) c2 = *(((const unsigned char *)s2) - 1); return (int)c1 - (int)c2; } -#endif /* ARCH_STRING_STRCMP */ +#endif /* STRING_ARCH_STRCMP */ diff --git a/arch/x86/machine/string.h b/arch/x86/machine/string.h new file mode 100644 index 0000000..d57bb24 --- /dev/null +++ b/arch/x86/machine/string.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 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 3 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_STRING_H +#define _X86_STRING_H + +/* + * Provide architecture-specific string functions. + */ +#define STRING_ARCH_MEMCPY +#define STRING_ARCH_MEMMOVE +#define STRING_ARCH_MEMSET +#define STRING_ARCH_MEMCMP +#define STRING_ARCH_STRLEN +#define STRING_ARCH_STRCPY +#define STRING_ARCH_STRCMP + +#endif /* _X86_STRING_H */ diff --git a/kern/string.c b/kern/string.c index 584030b..763012a 100644 --- a/kern/string.c +++ b/kern/string.c @@ -21,9 +21,9 @@ #include #include -#include +#include -#ifndef ARCH_STRING_MEMCPY +#ifndef STRING_ARCH_MEMCPY void * memcpy(void *dest, const void *src, size_t n) { @@ -40,9 +40,9 @@ memcpy(void *dest, const void *src, size_t n) return dest; } -#endif /* ARCH_STRING_MEMCPY */ +#endif /* STRING_ARCH_MEMCPY */ -#ifndef ARCH_STRING_MEMMOVE +#ifndef STRING_ARCH_MEMMOVE void * memmove(void *dest, const void *src, size_t n) { @@ -68,9 +68,9 @@ memmove(void *dest, const void *src, size_t n) return dest; } -#endif /* ARCH_STRING_MEMMOVE */ +#endif /* STRING_ARCH_MEMMOVE */ -#ifndef ARCH_STRING_MEMSET +#ifndef STRING_ARCH_MEMSET void * memset(void *s, int c, size_t n) { @@ -85,9 +85,9 @@ memset(void *s, int c, size_t n) return s; } -#endif /* ARCH_STRING_MEMSET */ +#endif /* STRING_ARCH_MEMSET */ -#ifndef ARCH_STRING_MEMCMP +#ifndef STRING_ARCH_MEMCMP int memcmp(const void *s1, const void *s2, size_t n) { @@ -104,9 +104,9 @@ memcmp(const void *s1, const void *s2, size_t n) return 0; } -#endif /* ARCH_STRING_MEMCMP */ +#endif /* STRING_ARCH_MEMCMP */ -#ifndef ARCH_STRING_STRLEN +#ifndef STRING_ARCH_STRLEN size_t strlen(const char *s) { @@ -120,9 +120,9 @@ strlen(const char *s) return i; } -#endif /* ARCH_STRING_STRLEN */ +#endif /* STRING_ARCH_STRLEN */ -#ifndef ARCH_STRING_STRCPY +#ifndef STRING_ARCH_STRCPY char * strcpy(char *dest, const char *src) { @@ -137,7 +137,7 @@ strcpy(char *dest, const char *src) return tmp; } -#endif /* ARCH_STRING_STRCPY */ +#endif /* STRING_ARCH_STRCPY */ size_t strlcpy(char *dest, const char *src, size_t n) @@ -158,7 +158,7 @@ out: return len; } -#ifndef ARCH_STRING_STRCMP +#ifndef STRING_ARCH_STRCMP int strcmp(const char *s1, const char *s2) { @@ -175,9 +175,9 @@ strcmp(const char *s1, const char *s2) return (int)c1 - (int)c2; } -#endif /* ARCH_STRING_STRCMP */ +#endif /* STRING_ARCH_STRCMP */ -#ifndef ARCH_STRING_STRNCMP +#ifndef STRING_ARCH_STRNCMP int strncmp(const char *s1, const char *s2, size_t n) { @@ -198,9 +198,9 @@ strncmp(const char *s1, const char *s2, size_t n) return (int)c1 - (int)c2; } -#endif /* ARCH_STRING_STRNCMP */ +#endif /* STRING_ARCH_STRNCMP */ -#ifndef ARCH_STRING_STRCHR +#ifndef STRING_ARCH_STRCHR char * strchr(const char *s, int c) { @@ -214,4 +214,4 @@ strchr(const char *s, int c) s++; } } -#endif /* ARCH_STRING_STRCHR */ +#endif /* STRING_ARCH_STRCHR */ -- cgit v1.2.3 From 49f414edfa049023b5f68f272a1f2e512be4a598 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 22 Jun 2017 23:57:51 +0200 Subject: kern/macros: move the __read_mostly macro here --- arch/x86/machine/param.h | 5 ----- arch/x86/machine/strace.c | 2 +- kern/cpumap.c | 2 +- kern/llsync.c | 1 - kern/macros.h | 5 +++++ kern/percpu.c | 1 - kern/task.c | 2 +- vm/vm_kmem.c | 1 + vm/vm_kmem.h | 1 + 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index 557bc80..86ce81b 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -46,11 +46,6 @@ #define DATA_ALIGN 4 #endif /* __LP64__ */ -/* - * Attributes for variables that are mostly read and seldom changed. - */ -#define __read_mostly __section(".data.read_mostly") - /* * System timer frequency. * diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index 6c28cf1..4b7e743 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/kern/cpumap.c b/kern/cpumap.c index 5582e68..e4ae864 100644 --- a/kern/cpumap.c +++ b/kern/cpumap.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include static struct cpumap cpumap_active_cpus __read_mostly = { { 1 } }; diff --git a/kern/llsync.c b/kern/llsync.c index 4704e80..e339869 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/macros.h b/kern/macros.h index 8f0d10c..63ded05 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -25,6 +25,11 @@ #include #endif /* __ASSEMBLER__ */ +/* + * Attributes for variables that are mostly read and seldom changed. + */ +#define __read_mostly __section(".data.read_mostly") + #define MACRO_BEGIN ({ #define MACRO_END }) diff --git a/kern/percpu.c b/kern/percpu.c index 805825a..c21376f 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/task.c b/kern/task.c index a528199..af3cb4c 100644 --- a/kern/task.c +++ b/kern/task.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index a9ba842..8139ddb 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index c4b5295..30c7cd3 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -20,6 +20,7 @@ #include +#include #include /* -- cgit v1.2.3 From e7b33bf55de53c7109687ce79928435dbe4c4032 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 20:21:36 +0200 Subject: x86/cga: fix newline character handling --- arch/x86/machine/cga.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index e364b91..7dca6ed 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -296,7 +296,7 @@ static void cga_bbuf_newline(struct cga_bbuf *bbuf) { uint16_t cursor = 0, spaces[CGA_COLUMNS]; - size_t i, nr_spaces, offset; + size_t i, nr_spaces, offset, size; int error; cga_bbuf_reset_view(bbuf); @@ -310,6 +310,13 @@ cga_bbuf_newline(struct cga_bbuf *bbuf) spaces[i] = cga_build_cell(' '); } + /* + * The cursor may not point at the end of the view, in which case + * any existing data must be preserved. + */ + size = sizeof(spaces); + cbuf_read(&bbuf->cbuf, bbuf->cursor, spaces, &size); + cbuf_write(&bbuf->cbuf, bbuf->cursor, spaces, nr_spaces * sizeof(spaces[0])); offset = cga_bbuf_cursor_offset(bbuf); -- cgit v1.2.3 From e6459c6060cb952c81ff1bfb6e33c38d6c451da4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 20:21:36 +0200 Subject: x86/atkbd: implement the home/end keys --- arch/x86/machine/atkbd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index bee0195..fd80426 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -367,12 +367,16 @@ static const char *atkbd_chars[] = { [ATKBD_KEY_SPACE] = " ", [ATKBD_KEY_DELETE] = "\e[3~", + [ATKBD_KEY_HOME] = "\e[H", + [ATKBD_KEY_END] = "\e[F", [ATKBD_KEY_KP_SLASH] = "/", [ATKBD_KEY_KP_STAR] = "*", [ATKBD_KEY_KP_MINUS] = "-", + [ATKBD_KEY_KP_HOME] = "\e[H", [ATKBD_KEY_KP_PLUS] = "+", [ATKBD_KEY_KP_5] = "5", + [ATKBD_KEY_KP_END] = "\e[F", [ATKBD_KEY_KP_ENTER] = "\n", [ATKBD_KEY_KP_DEL] = "\e[3~", }; -- cgit v1.2.3 From ff71d5acbd60fd9f0009fcf675f22a6ab01ac5ad Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 20:21:36 +0200 Subject: kern/cbuf: fix cbuf_range_valid --- kern/cbuf.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kern/cbuf.h b/kern/cbuf.h index a30e5f2..4d69334 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -70,7 +70,9 @@ cbuf_clear(struct cbuf *cbuf) static inline bool cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end) { - return ((end - start) <= cbuf_size(cbuf)); + return (((end - start) <= cbuf_size(cbuf)) + && ((start - cbuf->start) <= cbuf_size(cbuf)) + && ((cbuf->end - end) <= cbuf_size(cbuf))); } /* -- cgit v1.2.3 From f880f11cfa1d0c309b63a19e71c0033f45dc3122 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 23:23:30 +0200 Subject: kern/log: use cbuf_range_valid where appropriate --- kern/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/log.c b/kern/log.c index 135d402..adba7d3 100644 --- a/kern/log.c +++ b/kern/log.c @@ -431,7 +431,7 @@ log_write(const void *s, size_t size) error = cbuf_write(&log_cbuf, cbuf_end(&log_cbuf), s, size); assert(!error); - if ((log_index - cbuf_start(&log_cbuf)) > cbuf_end(&log_cbuf)) { + if (!cbuf_range_valid(&log_cbuf, log_index, log_index + 1)) { log_nr_overruns += cbuf_start(&log_cbuf) - log_index; log_index = cbuf_start(&log_cbuf); } -- cgit v1.2.3 From eb68987d17b97e1f9e7a387e962bfcc8114873a3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 23:24:10 +0200 Subject: x86/cga: minor checks and improvements --- arch/x86/machine/cga.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 7dca6ed..b59c40f 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -82,6 +82,10 @@ static void *cga_memory __read_mostly; #define CGA_BACK_BUFFER_SIZE (64 * 1024) +#if CGA_BACK_BUFFER_SIZE < CGA_MEMORY_SIZE +#error "back buffer size must be at least as large as video memory" +#endif + #define CGA_SCROLL_PAGE ((CGA_LINES / 2) * CGA_MEMORY_LINE_SIZE) /* @@ -115,11 +119,9 @@ cga_get_cursor_position(void) { uint16_t tmp; - io_write_byte(CGA_PORT_CRTC_ADDR, - CGA_CRTC_CURSOR_LOC_HIGH_REG); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_LOC_HIGH_REG); tmp = io_read_byte(CGA_PORT_CRTC_DATA) << 8; - io_write_byte(CGA_PORT_CRTC_ADDR, - CGA_CRTC_CURSOR_LOC_LOW_REG); + io_write_byte(CGA_PORT_CRTC_ADDR, CGA_CRTC_CURSOR_LOC_LOW_REG); tmp |= io_read_byte(CGA_PORT_CRTC_DATA); return tmp; @@ -394,10 +396,9 @@ cga_bbuf_scroll_down(struct cga_bbuf *bbuf) size_t end; bbuf->view += CGA_SCROLL_PAGE; + end = bbuf->view + CGA_MEMORY_SIZE; - end = bbuf->view + CGA_MEMORY_SIZE - cbuf_start(&bbuf->cbuf); - - if (end > cbuf_size(&bbuf->cbuf)) { + if (!cbuf_range_valid(&bbuf->cbuf, bbuf->view, end)) { bbuf->view = cbuf_end(&bbuf->cbuf) - CGA_MEMORY_SIZE; } -- cgit v1.2.3 From 642b0dc8dbf1d49f7b4ab6fa9f6dfb23cc7b06e1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 23:26:05 +0200 Subject: kern/fmt: fix sscanf character conversion reporting --- kern/fmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/fmt.c b/kern/fmt.c index 2adec7f..34424e2 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -1317,7 +1317,7 @@ fmt_sscanf_state_produce_char(struct fmt_sscanf_state *state) fmt_sscanf_state_restore_string(state); } - if (dest != NULL) { + if ((dest != NULL) && (i != 0)) { fmt_sscanf_state_report_conv(state); } -- cgit v1.2.3 From ff72cb26d8f9da08c45652011687f6684566cf3b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 23 Jun 2017 23:27:39 +0200 Subject: x86/uart: handle command line arguments --- arch/x86/machine/uart.c | 153 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 146 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index 22d3948..cdb49a9 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -13,14 +13,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * - * TODO Make serial line parameters configurable. */ #include #include +#include #include #include #include @@ -53,6 +51,8 @@ #define UART_LCR_8BITS 0x03 #define UART_LCR_1S 0x00 #define UART_LCR_NP 0x00 +#define UART_LCR_OP 0x08 +#define UART_LCR_EP 0x18 #define UART_LCR_BEN 0x40 #define UART_LCR_DLAB 0x80 @@ -65,6 +65,19 @@ #define UART_MAX_DEVS 4 +#define UART_SPEED_MAX 115200 +#define UART_SPEED_DEFAULT UART_SPEED_MAX + +enum { + UART_PARITY_NONE, + UART_PARITY_ODD, + UART_PARITY_EVEN, +}; + +#define UART_PARITY_DEFAULT UART_PARITY_NONE + +#define UART_DATA_BITS_DEFAULT 8 + struct uart { struct console console; uint16_t port; @@ -242,28 +255,154 @@ static const struct console_ops uart_console_ops = { .putc = uart_console_putc, }; +static void __init +uart_init_default(unsigned int *speed, unsigned int *parity, + unsigned int *data_bits) +{ + *speed = UART_SPEED_DEFAULT; + *parity = UART_PARITY_DEFAULT; + *data_bits = UART_DATA_BITS_DEFAULT; +} + +static int __init +uart_init_check_speed(unsigned int speed) +{ + if (speed > UART_SPEED_MAX) { + return ERROR_INVAL; + } + + return 0; +} + +static int __init +uart_init_convert_parity_char(char c, unsigned int *parity) +{ + switch (c) { + case 'n': + *parity = UART_PARITY_NONE; + break; + case 'o': + *parity = UART_PARITY_ODD; + break; + case 'e': + *parity = UART_PARITY_EVEN; + break; + default: + return ERROR_INVAL; + } + + return 0; +} + +static int __init +uart_init_check_data_bits(unsigned int data_bits) +{ + switch (data_bits) { + case 5 ... 8: + break; + default: + return ERROR_INVAL; + } + + return 0; +} + +static void __init +uart_init_args(const struct uart *uart, const char *arg_str, + unsigned int *speed, unsigned int *parity, + unsigned int *data_bits) +{ + char parity_char; + int ret, error; + + ret = sscanf(arg_str, "%u%c%1u", speed, &parity_char, data_bits); + + if (ret < 1) { + goto set_defaults; + } + + error = uart_init_check_speed(*speed); + + if (error) { + goto set_defaults; + } else if (ret < 2) { + return; + } + + error = uart_init_convert_parity_char(parity_char, parity); + + if (error) { + goto set_defaults; + } else if (ret < 3) { + return; + } + + error = uart_init_check_data_bits(*data_bits); + + if (error) { + goto set_defaults; + } + + return; + +set_defaults: + log_warning("uart%zu: invalid serial configuration, using defaults", + uart_get_id(uart)); + uart_init_default(speed, parity, data_bits); +} + static void __init uart_init(struct uart *uart, uint16_t port, uint16_t intr) { + unsigned int speed, parity, data_bits; + const char *arg_str; char name[CONSOLE_NAME_SIZE]; + uint16_t divisor; uint8_t byte; + snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); + arg_str = arg_value(name); + + uart_init_default(&speed, &parity, &data_bits); + + if (arg_str != NULL) { + uart_init_args(uart, arg_str, &speed, &parity, &data_bits); + } + + log_debug("uart%zu: speed:%u parity:%u data_bits:%u", + uart_get_id(uart), speed, parity, data_bits); + uart->port = port; uart->intr = intr; uart_write(uart, UART_REG_IER, 0); + divisor = UART_SPEED_MAX / speed; + uart_set(uart, UART_REG_LCR, UART_LCR_DLAB); - uart_write(uart, UART_REG_DLH, 0); - uart_write(uart, UART_REG_DLL, 1); + uart_write(uart, UART_REG_DLH, divisor >> 8); + uart_write(uart, UART_REG_DLL, divisor & 0xff); uart_clear(uart, UART_REG_LCR, UART_LCR_DLAB); uart_write(uart, UART_REG_MCR, UART_MCR_AUX2 | UART_MCR_RTS | UART_MCR_DTR); - byte = UART_LCR_8BITS | UART_LCR_NP | UART_LCR_1S; + byte = UART_LCR_1S; + + switch (parity) { + case UART_PARITY_NONE: + byte |= UART_LCR_NP; + break; + case UART_PARITY_ODD: + byte |= UART_LCR_OP; + break; + case UART_PARITY_EVEN: + byte |= UART_LCR_EP; + break; + } + + byte |= (data_bits - 5); uart_write(uart, UART_REG_LCR, byte); - snprintf(name, sizeof(name), "uart%zu", uart_get_id(uart)); console_init(&uart->console, name, &uart_console_ops); console_register(&uart->console); } -- cgit v1.2.3 From f452d261c6e3394224bc5b44691e70129981b2ef Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 01:16:38 +0200 Subject: kern/kmem: implement the kmem_info shell command --- kern/kmem.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/kern/kmem.c b/kern/kmem.c index 7c94cb1..b3c2978 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -1092,18 +1093,6 @@ kmem_cache_info(struct kmem_cache *cache) { char flags_str[64]; - if (cache == NULL) { - mutex_lock(&kmem_cache_list_lock); - - list_for_each_entry(&kmem_cache_list, cache, node) { - kmem_cache_info(cache); - } - - mutex_unlock(&kmem_cache_list_lock); - - return; - } - snprintf(flags_str, sizeof(flags_str), "%s%s", (cache->flags & KMEM_CF_SLAB_EXTERNAL) ? " SLAB_EXTERNAL" : "", (cache->flags & KMEM_CF_VERIFY) ? " VERIFY" : ""); @@ -1135,6 +1124,56 @@ kmem_cache_info(struct kmem_cache *cache) mutex_unlock(&cache->lock); } +#ifdef X15_SHELL + +static struct kmem_cache * +kmem_lookup_cache(const char *name) +{ + struct kmem_cache *cache; + + mutex_lock(&kmem_cache_list_lock); + + list_for_each_entry(&kmem_cache_list, cache, node) { + if (strcmp(cache->name, name) == 0) { + goto out; + } + } + + cache = NULL; + +out: + mutex_unlock(&kmem_cache_list_lock); + + return cache; +} + +static void +kmem_shell_info(int argc, char **argv) +{ + struct kmem_cache *cache; + + if (argc < 2) { + kmem_info(); + } else { + cache = kmem_lookup_cache(argv[1]); + + if (cache == NULL) { + printf("kmem: info: invalid argument\n"); + return; + } + + kmem_cache_info(cache); + } +} + +static struct shell_cmd kmem_shell_cmds[] = { + SHELL_CMD_INITIALIZER("kmem_info", kmem_shell_info, + "kmem_info []", + "print information about kernel memory and caches"), +}; + +#endif /* X15_SHELL */ + void __init kmem_setup(void) { @@ -1170,6 +1209,8 @@ kmem_setup(void) kmem_cache_init(&kmem_caches[i], name, size, 0, NULL, 0); size <<= 1; } + + SHELL_REGISTER_CMDS(kmem_shell_cmds); } static inline size_t -- cgit v1.2.3 From 693e762d0d2f6bd63d98905404d0f6cc8ccc8115 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 01:26:28 +0200 Subject: vm/vm_page: implement the vm_page_info shell command --- kern/kernel.c | 2 +- vm/vm_page.c | 57 +++++++++++++++++++++++++++++++++++++++++++++------------ vm/vm_page.h | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/kern/kernel.c b/kern/kernel.c index 326c70a..9aa5c35 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -54,7 +54,7 @@ kernel_main(void) work_setup(); llsync_setup(); sref_setup(); - vm_page_info(); + vm_page_log_info(); log_start(); #ifdef X15_RUN_TEST_MODULE diff --git a/vm/vm_page.c b/vm/vm_page.c index dd17573..b675319 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -634,6 +635,46 @@ vm_page_bootalloc(size_t size) panic("vm_page: no physical memory available"); } +static void +vm_page_info_common(int (*print_fn)(const char *format, ...)) +{ + struct vm_page_zone *zone; + unsigned long pages; + unsigned int i; + + for (i = 0; i < vm_page_zones_size; i++) { + zone = &vm_page_zones[i]; + pages = (unsigned long)(zone->pages_end - zone->pages); + print_fn("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)\n", + vm_page_zone_name(i), pages, pages >> (20 - PAGE_SHIFT), + zone->nr_free_pages, zone->nr_free_pages >> (20 - PAGE_SHIFT)); + } +} + +#ifdef X15_SHELL + +static void +vm_page_info(void) +{ + vm_page_info_common(printf); +} + +static void +vm_page_shell_info(int argc, char **argv) +{ + (void)argc; + (void)argv; + vm_page_info(); +} + +static struct shell_cmd vm_page_shell_cmds[] = { + SHELL_CMD_INITIALIZER("vm_page_info", vm_page_shell_info, + "vm_page_info", + "print information about physical memory"), +}; + +#endif /* X15_SHELL */ + void __init vm_page_setup(void) { @@ -694,6 +735,8 @@ vm_page_setup(void) } vm_page_is_ready = 1; + + SHELL_REGISTER_CMDS(vm_page_shell_cmds); } void __init @@ -766,17 +809,7 @@ vm_page_zone_name(unsigned int zone_index) } void -vm_page_info(void) +vm_page_log_info(void) { - struct vm_page_zone *zone; - unsigned long pages; - unsigned int i; - - for (i = 0; i < vm_page_zones_size; i++) { - zone = &vm_page_zones[i]; - pages = (unsigned long)(zone->pages_end - zone->pages); - log_info("vm_page: %s: pages: %lu (%luM), free: %lu (%luM)", - vm_page_zone_name(i), pages, pages >> (20 - PAGE_SHIFT), - zone->nr_free_pages, zone->nr_free_pages >> (20 - PAGE_SHIFT)); - } + vm_page_info_common(log_info); } diff --git a/vm/vm_page.h b/vm/vm_page.h index fa018a0..34ad163 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -209,6 +209,6 @@ const char * vm_page_zone_name(unsigned int zone_index); /* * Display internal information about the module. */ -void vm_page_info(void); +void vm_page_log_info(void); #endif /* _VM_VM_PAGE_H */ -- cgit v1.2.3 From aeea74bc5438895f09329ef1a3a006f251ae25ce Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 01:28:08 +0200 Subject: vm/vm_map: implement the vm_map_info shell command --- kern/task.h | 6 ++++++ vm/vm_map.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/kern/task.h b/kern/task.h index cfe694b..591e8d7 100644 --- a/kern/task.h +++ b/kern/task.h @@ -68,6 +68,12 @@ task_unref(struct task *task) } } +static inline struct vm_map * +task_get_vm_map(const struct task *task) +{ + return task->map; +} + /* * Initialize the task module. */ diff --git a/vm/vm_map.c b/vm/vm_map.c index f506d3b..d7c7fa6 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -695,6 +697,39 @@ vm_map_init(struct vm_map *map, struct pmap *pmap, map->pmap = pmap; } +#ifdef X15_SHELL + +static void +vm_map_shell_info(int argc, char **argv) +{ + const struct task *task; + + if (argc < 2) { + goto error; + } else { + task = task_lookup(argv[1]); + + if (task == NULL) { + goto error; + } + + vm_map_info(task_get_vm_map(task)); + } + + return; + +error: + printf("vm_map: info: invalid arguments\n"); +} + +static struct shell_cmd vm_map_shell_cmds[] = { + SHELL_CMD_INITIALIZER("vm_map_info", vm_map_shell_info, + "vm_map_info ", + "print information about a VM map"), +}; + +#endif /* X15_SHELL */ + void __init vm_map_setup(void) { @@ -705,6 +740,7 @@ vm_map_setup(void) KMEM_CACHE_PAGE_ONLY); kmem_cache_init(&vm_map_cache, "vm_map", sizeof(struct vm_map), 0, NULL, 0); + SHELL_REGISTER_CMDS(vm_map_shell_cmds); } int -- cgit v1.2.3 From a2729aef568285daff96ffc5719caf8e26043ca9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 01:31:08 +0200 Subject: kern/log: force log messages to fit in a single line --- kern/log.c | 9 +++++++++ kern/log.h | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/kern/log.c b/kern/log.c index adba7d3..96be152 100644 --- a/kern/log.c +++ b/kern/log.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -457,6 +458,7 @@ log_vmsg(unsigned int level, const char *format, va_list ap) unsigned long flags; int nr_chars; size_t size; + char *ptr; log_record_init_produce(&record, level); nr_chars = vsnprintf(record.buffer, sizeof(record.buffer), format, ap); @@ -466,6 +468,13 @@ log_vmsg(unsigned int level, const char *format, va_list ap) goto out; } + ptr = strchr(record.buffer, '\n'); + + if (ptr != NULL) { + *ptr = '\0'; + nr_chars = ptr - record.buffer; + } + assert(nr_chars >= 0); size = offsetof(struct log_record, buffer) + nr_chars + 1; diff --git a/kern/log.h b/kern/log.h index a306ba3..003e99b 100644 --- a/kern/log.h +++ b/kern/log.h @@ -48,7 +48,11 @@ void log_start(void); /* * Generate a message and send it to the log thread. * - * Except for level, the arguments and return value are similar to printf(). + * The arguments and return value are similar to printf(), with + * these exceptions : + * - a level is associated to each log message + * - processing stops at the first terminating null byte or newline + * character, whichever occurs first * * This function may safely be called in interrupt context. */ -- cgit v1.2.3 From fbc0e2bb5a835e03bf5dfa1d895b1f2d5f4441fe Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 17:30:58 +0200 Subject: kern/syscnt: update console output format --- kern/syscnt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kern/syscnt.c b/kern/syscnt.c index a9c13f8..762f143 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -88,8 +88,6 @@ syscnt_info(const char *prefix) prefix_length = (prefix == NULL) ? 0 : strlen(prefix); - printf("syscnt: name value\n"); - mutex_lock(&syscnt_lock); list_for_each_entry(&syscnt_list, syscnt, node) { @@ -104,7 +102,7 @@ syscnt_info(const char *prefix) value = syscnt_read(syscnt); - printf("syscnt: %-30s %17llu\n", syscnt->name, + printf("syscnt: %40s %20llu\n", syscnt->name, (unsigned long long)value); } -- cgit v1.2.3 From 13de6a8b39d2de95d37ae4a070481385dec4bf9c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 17:27:56 +0200 Subject: kern/kmem: update console output format --- kern/kmem.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/kern/kmem.c b/kern/kmem.c index b3c2978..6aded09 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -1099,23 +1099,22 @@ kmem_cache_info(struct kmem_cache *cache) mutex_lock(&cache->lock); - printf("kmem: name: %s\n" - "kmem: flags: 0x%x%s\n" - "kmem: obj_size: %zu\n" - "kmem: align: %zu\n" - "kmem: buf_size: %zu\n" - "kmem: bufctl_dist: %zu\n" - "kmem: slab_size: %zu\n" - "kmem: color_max: %zu\n" + printf("kmem: flags: 0x%x%s\n" + "kmem: obj_size: %zu\n" + "kmem: align: %zu\n" + "kmem: buf_size: %zu\n" + "kmem: bufctl_dist: %zu\n" + "kmem: slab_size: %zu\n" + "kmem: color_max: %zu\n" "kmem: bufs_per_slab: %lu\n" - "kmem: nr_objs: %lu\n" - "kmem: nr_bufs: %lu\n" - "kmem: nr_slabs: %lu\n" + "kmem: nr_objs: %lu\n" + "kmem: nr_bufs: %lu\n" + "kmem: nr_slabs: %lu\n" "kmem: nr_free_slabs: %lu\n" - "kmem: buftag_dist: %zu\n" - "kmem: redzone_pad: %zu\n" - "kmem: cpu_pool_size: %d\n", cache->name, cache->flags, flags_str, - cache->obj_size, cache->align, cache->buf_size, cache->bufctl_dist, + "kmem: buftag_dist: %zu\n" + "kmem: redzone_pad: %zu\n" + "kmem: cpu_pool_size: %d\n", cache->flags, flags_str, cache->obj_size, + cache->align, cache->buf_size, cache->bufctl_dist, cache->slab_size, cache->color_max, cache->bufs_per_slab, cache->nr_objs, cache->nr_bufs, cache->nr_slabs, cache->nr_free_slabs, cache->buftag_dist, cache->redzone_pad, @@ -1323,8 +1322,11 @@ kmem_free(void *ptr, size_t size) void kmem_info(void) { + size_t mem_usage, mem_reclaimable, total, total_reclaimable; struct kmem_cache *cache; - size_t mem_usage, mem_reclaimable; + + total = 0; + total_reclaimable = 0; printf("kmem: cache obj slab bufs objs bufs " " total reclaimable\n" @@ -1338,6 +1340,8 @@ kmem_info(void) mem_usage = (cache->nr_slabs * cache->slab_size) >> 10; mem_reclaimable = (cache->nr_free_slabs * cache->slab_size) >> 10; + total += mem_usage; + total_reclaimable += mem_reclaimable; printf("kmem: %-19s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", cache->name, cache->obj_size, cache->slab_size >> 10, @@ -1348,4 +1352,6 @@ kmem_info(void) } mutex_unlock(&kmem_cache_list_lock); + + printf("total: %zuk reclaimable: %zuk\n", total, total_reclaimable); } -- cgit v1.2.3 From d59e9a82fd76a1c585cdb55bb5ccb5228ce83646 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 17:51:37 +0200 Subject: Make shell command descriptions consistent --- kern/kmem.c | 2 +- kern/shell.c | 2 +- kern/syscnt.c | 2 +- kern/task.c | 2 +- kern/thread.c | 2 +- vm/vm_map.c | 2 +- vm/vm_page.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kern/kmem.c b/kern/kmem.c index 6aded09..9055731 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -1168,7 +1168,7 @@ kmem_shell_info(int argc, char **argv) static struct shell_cmd kmem_shell_cmds[] = { SHELL_CMD_INITIALIZER("kmem_info", kmem_shell_info, "kmem_info []", - "print information about kernel memory and caches"), + "display information about kernel memory and caches"), }; #endif /* X15_SHELL */ diff --git a/kern/shell.c b/kern/shell.c index c526b31..81fe013 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -692,7 +692,7 @@ shell_cmd_history(int argc, char *argv[]) static struct shell_cmd shell_default_cmds[] = { SHELL_CMD_INITIALIZER("help", shell_cmd_help, "help [command]", - "obtain help about shell commands"), + "display help about shell commands"), SHELL_CMD_INITIALIZER("history", shell_cmd_history, "history", "display history list"), diff --git a/kern/syscnt.c b/kern/syscnt.c index 762f143..d5fd28a 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -54,7 +54,7 @@ syscnt_shell_info(int argc, char **argv) static struct shell_cmd syscnt_shell_cmds[] = { SHELL_CMD_INITIALIZER("syscnt_info", syscnt_shell_info, "syscnt_info []", - "show information about system counters"), + "display information about system counters"), }; #endif /* X15_SHELL */ diff --git a/kern/task.c b/kern/task.c index af3cb4c..9764cfe 100644 --- a/kern/task.c +++ b/kern/task.c @@ -96,7 +96,7 @@ error: static struct shell_cmd task_shell_cmds[] = { SHELL_CMD_INITIALIZER("task_info", task_shell_info, "task_info []", - "print tasks and threads"), + "display tasks and threads"), }; #endif /* X15_SHELL */ diff --git a/kern/thread.c b/kern/thread.c index 713c28f..e290be2 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2287,7 +2287,7 @@ error: static struct shell_cmd thread_shell_cmds[] = { SHELL_CMD_INITIALIZER("thread_trace", thread_shell_trace, "thread_trace ", - "print the stack trace of a given thread"), + "display the stack trace of a given thread"), }; #endif /* X15_SHELL */ diff --git a/vm/vm_map.c b/vm/vm_map.c index d7c7fa6..abe2aa8 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -725,7 +725,7 @@ error: static struct shell_cmd vm_map_shell_cmds[] = { SHELL_CMD_INITIALIZER("vm_map_info", vm_map_shell_info, "vm_map_info ", - "print information about a VM map"), + "display information about a VM map"), }; #endif /* X15_SHELL */ diff --git a/vm/vm_page.c b/vm/vm_page.c index b675319..4ddf34a 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -670,7 +670,7 @@ vm_page_shell_info(int argc, char **argv) static struct shell_cmd vm_page_shell_cmds[] = { SHELL_CMD_INITIALIZER("vm_page_info", vm_page_shell_info, "vm_page_info", - "print information about physical memory"), + "display information about physical memory"), }; #endif /* X15_SHELL */ -- cgit v1.2.3 From 23b2232e1208cc00d8d2f36e5ad69d779cd27510 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 20:10:26 +0200 Subject: kern/kmem: use predefined compiler endianness macros --- kern/kmem_i.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/kern/kmem_i.h b/kern/kmem_i.h index f3ad822..28dccd1 100644 --- a/kern/kmem_i.h +++ b/kern/kmem_i.h @@ -70,17 +70,17 @@ union kmem_bufctl { * Redzone guard word. */ #ifdef __LP64__ -#ifdef __BIG_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define KMEM_REDZONE_WORD 0xfeedfacefeedfaceUL -#else /* __BIG_ENDIAN__ */ +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #define KMEM_REDZONE_WORD 0xcefaedfecefaedfeUL -#endif /* __BIG_ENDIAN__ */ +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #else /* __LP64__ */ -#ifdef __BIG_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define KMEM_REDZONE_WORD 0xfeedfaceUL -#else /* __BIG_ENDIAN__ */ +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #define KMEM_REDZONE_WORD 0xcefaedfeUL -#endif /* __BIG_ENDIAN__ */ +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #endif /* __LP64__ */ /* @@ -104,21 +104,21 @@ struct kmem_buftag { * Values the buftag state member can take. */ #ifdef __LP64__ -#ifdef __BIG_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define KMEM_BUFTAG_ALLOC 0xa110c8eda110c8edUL #define KMEM_BUFTAG_FREE 0xf4eeb10cf4eeb10cUL -#else /* __BIG_ENDIAN__ */ +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #define KMEM_BUFTAG_ALLOC 0xedc810a1edc810a1UL #define KMEM_BUFTAG_FREE 0x0cb1eef40cb1eef4UL -#endif /* __BIG_ENDIAN__ */ +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #else /* __LP64__ */ -#ifdef __BIG_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define KMEM_BUFTAG_ALLOC 0xa110c8edUL #define KMEM_BUFTAG_FREE 0xf4eeb10cUL -#else /* __BIG_ENDIAN__ */ +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #define KMEM_BUFTAG_ALLOC 0xedc810a1UL #define KMEM_BUFTAG_FREE 0x0cb1eef4UL -#endif /* __BIG_ENDIAN__ */ +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #endif /* __LP64__ */ /* @@ -127,13 +127,13 @@ struct kmem_buftag { * These values are unconditionally 64-bit wide since buffers are at least * 8-byte aligned. */ -#ifdef __BIG_ENDIAN__ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define KMEM_FREE_PATTERN 0xdeadbeefdeadbeefULL #define KMEM_UNINIT_PATTERN 0xbaddcafebaddcafeULL -#else /* __BIG_ENDIAN__ */ +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ #define KMEM_FREE_PATTERN 0xefbeaddeefbeaddeULL #define KMEM_UNINIT_PATTERN 0xfecaddbafecaddbaULL -#endif /* __BIG_ENDIAN__ */ +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ /* * Page-aligned collection of unconstructed buffers. -- cgit v1.2.3 From 3ec97de24bfaeb44fe2b0f7048062bbb496d2c7e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 20:10:54 +0200 Subject: x86/param: remove __LITTLE_ENDIAN__ macro --- arch/x86/machine/param.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index 86ce81b..042e46e 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -24,10 +24,6 @@ #include -#ifndef __LITTLE_ENDIAN__ -#define __LITTLE_ENDIAN__ -#endif /* __LITTLE_ENDIAN__ */ - /* * L1 cache line size. * -- cgit v1.2.3 From 3c861e6cbc77d7aefcc2594b65a7d24d0df83357 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 20:34:23 +0200 Subject: x86/boot: remove extern stack declarations --- arch/x86/machine/boot.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/machine/boot.h b/arch/x86/machine/boot.h index bd5eb2d..d10f0c2 100644 --- a/arch/x86/machine/boot.h +++ b/arch/x86/machine/boot.h @@ -69,9 +69,6 @@ extern char _boot; extern char _boot_end; -extern char boot_stack[STACK_SIZE]; -extern char boot_ap_stack[STACK_SIZE]; - /* * This variable contains the CPU ID of an AP during early initialization. */ -- cgit v1.2.3 From b958a89b516218433d5982f1decce867d915cee4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 21:18:28 +0200 Subject: Break STACK_SIZE macro into 3 different macros --- arch/x86/machine/boot.c | 4 ++-- arch/x86/machine/boot.h | 5 +++++ arch/x86/machine/boot_asm.S | 12 ++++++------ arch/x86/machine/cpu.c | 18 ++++++++++-------- arch/x86/machine/param.h | 5 ----- arch/x86/machine/tcb.c | 3 +-- arch/x86/machine/tcb.h | 6 ++++++ arch/x86/machine/trap.c | 2 +- arch/x86/machine/trap.h | 4 ++++ kern/thread.c | 6 +++--- 10 files changed, 38 insertions(+), 27 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 59f0d67..72c5bb3 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -77,8 +77,8 @@ #include #include -char boot_stack[STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; -char boot_ap_stack[STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; +char boot_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; +char boot_ap_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; unsigned int boot_ap_id __bootdata; #ifdef __LP64__ diff --git a/arch/x86/machine/boot.h b/arch/x86/machine/boot.h index d10f0c2..c207059 100644 --- a/arch/x86/machine/boot.h +++ b/arch/x86/machine/boot.h @@ -21,6 +21,11 @@ #include #include +/* + * Size of the stack used when booting a processor. + */ +#define BOOT_STACK_SIZE PAGE_SIZE + /* * Macros used by the very early panic functions. */ diff --git a/arch/x86/machine/boot_asm.S b/arch/x86/machine/boot_asm.S index dc9c9a4..4393c13 100644 --- a/arch/x86/machine/boot_asm.S +++ b/arch/x86/machine/boot_asm.S @@ -67,7 +67,7 @@ ASM_ENTRY(_start) ljmp $BOOT_GDT_SEL_CODE, $1f 1: - movl $(boot_stack + STACK_SIZE), %esp + movl $(boot_stack + BOOT_STACK_SIZE), %esp #ifdef __LP64__ call boot_check_long_mode @@ -99,7 +99,7 @@ ASM_ENTRY(_start) #ifdef __LP64__ movq %rax, %cr3 - movq $(boot_stack + STACK_SIZE), %rsp + movq $(boot_stack + BOOT_STACK_SIZE), %rsp #else /* __LP64__ */ movl %eax, %cr3 movl %cr0, %eax @@ -108,7 +108,7 @@ ASM_ENTRY(_start) ljmp $BOOT_GDT_SEL_CODE, $1f 1: - movl $(boot_stack + STACK_SIZE), %esp + movl $(boot_stack + BOOT_STACK_SIZE), %esp #endif /* __LP64__ */ xorl %ebp, %ebp @@ -304,7 +304,7 @@ ASM_ENTRY(boot_ap_start32) ljmp $BOOT_GDT_SEL_CODE, $1f 1: - movl $(boot_ap_stack + STACK_SIZE), %esp + movl $(boot_ap_stack + BOOT_STACK_SIZE), %esp #ifdef __LP64__ call boot_setup_tls @@ -335,10 +335,10 @@ ASM_ENTRY(boot_ap_start32) #ifdef __LP64__ movq %rax, %rsp - addq $STACK_SIZE, %rsp + addq $BOOT_STACK_SIZE, %rsp #else /* __LP64__ */ movl %eax, %esp - addl $STACK_SIZE, %esp + addl $BOOT_STACK_SIZE, %esp #endif /* __LP64__ */ xorl %ebp, %ebp diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 4981ff6..83eaf90 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -144,7 +144,7 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; * memory. */ static unsigned long cpu_double_fault_handler; -static char cpu_double_fault_stack[STACK_SIZE] __aligned(DATA_ALIGN); +static char cpu_double_fault_stack[TRAP_STACK_SIZE] __aligned(DATA_ALIGN); void cpu_delay(unsigned long usecs) @@ -342,7 +342,7 @@ cpu_init_tss(struct cpu *cpu) #ifdef __LP64__ assert(cpu->double_fault_stack != NULL); tss->ist[CPU_TSS_IST_DF] = (unsigned long)cpu->double_fault_stack - + STACK_SIZE; + + TRAP_STACK_SIZE; #endif /* __LP64__ */ asm volatile("ltr %w0" : : "q" (CPU_GDT_SEL_TSS)); @@ -362,7 +362,7 @@ cpu_init_double_fault_tss(struct cpu *cpu) tss->cr3 = cpu_get_cr3(); tss->eip = cpu_double_fault_handler; tss->eflags = CPU_EFL_ONE; - tss->ebp = (unsigned long)cpu->double_fault_stack + STACK_SIZE; + tss->ebp = (unsigned long)cpu->double_fault_stack + TRAP_STACK_SIZE; tss->esp = tss->ebp; tss->es = CPU_GDT_SEL_DATA; tss->cs = CPU_GDT_SEL_CODE; @@ -414,7 +414,7 @@ cpu_idt_set_double_fault(void (*isr)(void)) static void cpu_load_idt(void) { - static volatile struct cpu_pseudo_desc idtr; + static volatile struct cpu_pseudo_desc idtr; /* TODO Review this */ idtr.address = (unsigned long)cpu_idt; idtr.limit = sizeof(cpu_idt) - 1; @@ -688,18 +688,20 @@ cpu_mp_setup(void) io_write_byte(CPU_MP_CMOS_PORT_REG, CPU_MP_CMOS_REG_RESET); io_write_byte(CPU_MP_CMOS_PORT_DATA, CPU_MP_CMOS_DATA_RESET_WARM); + /* TODO Allocate stacks out of the slab allocator for sub-page sizes */ + for (i = 1; i < cpu_count(); i++) { cpu = percpu_ptr(cpu_desc, i); - page = vm_page_alloc(vm_page_order(STACK_SIZE), VM_PAGE_SEL_DIRECTMAP, - VM_PAGE_KERNEL); + page = vm_page_alloc(vm_page_order(BOOT_STACK_SIZE), + VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL); if (page == NULL) { panic("cpu: unable to allocate boot stack for cpu%u", i); } cpu->boot_stack = vm_page_direct_ptr(page); - page = vm_page_alloc(vm_page_order(STACK_SIZE), VM_PAGE_SEL_DIRECTMAP, - VM_PAGE_KERNEL); + page = vm_page_alloc(vm_page_order(TRAP_STACK_SIZE), + VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL); if (page == NULL) { panic("cpu: unable to allocate double fault stack for cpu%u", i); diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index 042e46e..d8f0fc3 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -57,11 +57,6 @@ #define PAGE_SIZE (1 << PAGE_SHIFT) #define PAGE_MASK (PAGE_SIZE - 1) -/* - * Kernel stack size for threads and interrupt handlers. - */ -#define STACK_SIZE PAGE_SIZE - /* * Maximum number of available interrupts. */ diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index 5d16ea2..8604cee 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -85,7 +84,7 @@ tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) } tcb->bp = 0; - tcb->sp = (uintptr_t)stack + STACK_SIZE; + tcb->sp = (uintptr_t)stack + TCB_STACK_SIZE; tcb_stack_forge(tcb, fn, arg); return 0; } diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 0f67b76..0bb900c 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -25,8 +25,14 @@ #include #include +#include #include +/* + * Thread stack size. + */ +#define TCB_STACK_SIZE PAGE_SIZE + /* * Thread control block. */ diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index e033b35..ee3153f 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -37,7 +37,7 @@ #include struct trap_cpu_data { - unsigned char intr_stack[STACK_SIZE] __aligned(DATA_ALIGN); + unsigned char intr_stack[TRAP_STACK_SIZE] __aligned(DATA_ALIGN); }; static struct trap_cpu_data trap_cpu_data __percpu; diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index e4d5912..c2885c5 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -21,6 +21,8 @@ #ifndef _X86_TRAP_H #define _X86_TRAP_H +#include + /* * Architecture defined traps. */ @@ -63,6 +65,8 @@ #define TRAP_NR_VECTORS 256 +#define TRAP_STACK_SIZE PAGE_SIZE + #ifndef __ASSEMBLER__ #include diff --git a/kern/thread.c b/kern/thread.c index e290be2..7b186cb 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1893,7 +1893,7 @@ thread_alloc_stack(void) void *mem; int error; - stack_size = vm_page_round(STACK_SIZE); + stack_size = vm_page_round(TCB_STACK_SIZE); mem = vm_kmem_alloc((PAGE_SIZE * 2) + stack_size); if (mem == NULL) { @@ -1934,7 +1934,7 @@ thread_free_stack(void *stack) size_t stack_size; char *va; - stack_size = vm_page_round(STACK_SIZE); + stack_size = vm_page_round(TCB_STACK_SIZE); va = (char *)stack - PAGE_SIZE; vm_kmem_free_va(va, PAGE_SIZE); @@ -2304,7 +2304,7 @@ thread_setup(void) kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), CPU_L1_SIZE, NULL, 0); #ifndef X15_THREAD_STACK_GUARD - kmem_cache_init(&thread_stack_cache, "thread_stack", STACK_SIZE, + kmem_cache_init(&thread_stack_cache, "thread_stack", TCB_STACK_SIZE, DATA_ALIGN, NULL, 0); #endif /* X15_THREAD_STACK_GUARD */ -- cgit v1.2.3 From 50dc3017b08158f5a808b4817627bd6d8f957df3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 21:28:54 +0200 Subject: Move the CPU_L1_SIZE macro to x86/cpu module --- arch/x86/machine/cpu.h | 7 +++++++ arch/x86/machine/param.h | 7 ------- arch/x86/x15.lds.S | 1 + kern/kmem_i.h | 2 +- kern/llsync_i.h | 1 - kern/sleepq.c | 2 +- kern/thread_i.h | 2 +- kern/turnstile.c | 2 +- kern/work.c | 1 - kern/xcall.c | 1 - 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 6e0f19a..44d339c 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -18,6 +18,13 @@ #ifndef _X86_CPU_H #define _X86_CPU_H +/* + * L1 cache line size. + * + * XXX Use this value until processor selection is available. + */ +#define CPU_L1_SIZE 64 + /* * Processor privilege levels. */ diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index d8f0fc3..f8fe2c4 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -24,13 +24,6 @@ #include -/* - * L1 cache line size. - * - * XXX Use this value until processor selection is available. - */ -#define CPU_L1_SIZE 64 - /* * Code/data alignment. */ diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index 3fc57ca..bc7f480 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -9,6 +9,7 @@ OUTPUT_ARCH(i386) ENTRY(_start) #include +#include #include PHDRS diff --git a/kern/kmem_i.h b/kern/kmem_i.h index 28dccd1..d4671e2 100644 --- a/kern/kmem_i.h +++ b/kern/kmem_i.h @@ -22,7 +22,7 @@ #include #include -#include +#include /* * Per-processor cache of pre-constructed objects. diff --git a/kern/llsync_i.h b/kern/llsync_i.h index 12e2861..dd5a617 100644 --- a/kern/llsync_i.h +++ b/kern/llsync_i.h @@ -22,7 +22,6 @@ #include #include -#include #include #include #include diff --git a/kern/sleepq.c b/kern/sleepq.c index f5fa672..f7c3245 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -27,10 +27,10 @@ #include #include #include -#include #include #include #include +#include struct sleepq_bucket { struct spinlock lock; diff --git a/kern/thread_i.h b/kern/thread_i.h index 9c8d4fd..b7d3de1 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #include /* diff --git a/kern/turnstile.c b/kern/turnstile.c index 78532fb..97416ae 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -52,12 +52,12 @@ #include #include #include -#include #include #include #include #include #include +#include /* * Locking keys : diff --git a/kern/work.c b/kern/work.c index 284b1e2..9925c19 100644 --- a/kern/work.c +++ b/kern/work.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/xcall.c b/kern/xcall.c index ce22aa0..1c052ac 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 5127024f429da92ffdb93ba3cc0af367bc26f703 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 21:57:57 +0200 Subject: Move the DATA_ALIGN and TEXT_ALIGN macros to the x86/cpu module --- arch/x86/machine/asm.h | 6 +++--- arch/x86/machine/boot.c | 4 ++-- arch/x86/machine/cpu.c | 2 +- arch/x86/machine/cpu.h | 18 ++++++++++++++++++ arch/x86/machine/param.h | 11 ----------- arch/x86/machine/trap.c | 2 +- arch/x86/x15.lds.S | 4 ++-- kern/thread.c | 2 +- 8 files changed, 28 insertions(+), 21 deletions(-) diff --git a/arch/x86/machine/asm.h b/arch/x86/machine/asm.h index 6118bb8..204d6fe 100644 --- a/arch/x86/machine/asm.h +++ b/arch/x86/machine/asm.h @@ -22,16 +22,16 @@ #warning "asm.h included from a C file" #endif /* __ASSEMBLER__ */ -#include +#include #define ASM_ENTRY(x) \ -.align TEXT_ALIGN; \ +.align CPU_TEXT_ALIGN; \ .global x; \ .type x, STT_FUNC; \ x: #define ASM_DATA(x) \ -.align DATA_ALIGN; \ +.align CPU_DATA_ALIGN; \ .global x; \ .type x, STT_OBJECT; \ x: diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 72c5bb3..df7a206 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -77,8 +77,8 @@ #include #include -char boot_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; -char boot_ap_stack[BOOT_STACK_SIZE] __aligned(DATA_ALIGN) __bootdata; +char boot_stack[BOOT_STACK_SIZE] __aligned(CPU_DATA_ALIGN) __bootdata; +char boot_ap_stack[BOOT_STACK_SIZE] __aligned(CPU_DATA_ALIGN) __bootdata; unsigned int boot_ap_id __bootdata; #ifdef __LP64__ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 83eaf90..6baff62 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -144,7 +144,7 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; * memory. */ static unsigned long cpu_double_fault_handler; -static char cpu_double_fault_stack[TRAP_STACK_SIZE] __aligned(DATA_ALIGN); +static char cpu_double_fault_stack[TRAP_STACK_SIZE] __aligned(CPU_DATA_ALIGN); void cpu_delay(unsigned long usecs) diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 44d339c..0061e13 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -18,6 +18,8 @@ #ifndef _X86_CPU_H #define _X86_CPU_H +#include + /* * L1 cache line size. * @@ -25,6 +27,22 @@ */ #define CPU_L1_SIZE 64 +/* + * Data alignment, normally the word size. + */ +#define CPU_DATA_ALIGN (LONG_BIT / 8) + +/* + * Function alignment. + * + * Aligning functions improves instruction fetching. + * + * Used for assembly functions only. + * + * XXX Use this value until processor selection is available. + */ +#define CPU_TEXT_ALIGN 16 + /* * Processor privilege levels. */ diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index f8fe2c4..f9abaa4 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -24,17 +24,6 @@ #include -/* - * Code/data alignment. - */ -#define TEXT_ALIGN 16 - -#ifdef __LP64__ -#define DATA_ALIGN 8 -#else /* __LP64__ */ -#define DATA_ALIGN 4 -#endif /* __LP64__ */ - /* * System timer frequency. * diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index ee3153f..5950231 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -37,7 +37,7 @@ #include struct trap_cpu_data { - unsigned char intr_stack[TRAP_STACK_SIZE] __aligned(DATA_ALIGN); + unsigned char intr_stack[TRAP_STACK_SIZE] __aligned(CPU_DATA_ALIGN); }; static struct trap_cpu_data trap_cpu_data __percpu; diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index bc7f480..b5400c4 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -69,7 +69,7 @@ SECTIONS *(.rodata) } : rodata - .notes ALIGN(DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.notes))) { + .notes ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.notes))) { *(.note.*) } : rodata @@ -83,7 +83,7 @@ SECTIONS *(.data) } : data - .bss ALIGN(DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.bss))) { + .bss ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.bss))) { *(.bss) } : data diff --git a/kern/thread.c b/kern/thread.c index 7b186cb..785640c 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2305,7 +2305,7 @@ thread_setup(void) CPU_L1_SIZE, NULL, 0); #ifndef X15_THREAD_STACK_GUARD kmem_cache_init(&thread_stack_cache, "thread_stack", TCB_STACK_SIZE, - DATA_ALIGN, NULL, 0); + CPU_DATA_ALIGN, NULL, 0); #endif /* X15_THREAD_STACK_GUARD */ thread_setup_reaper(); -- cgit v1.2.3 From 7fc9d340e5ff385846c0c77fd24877c1b208a3bb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 22:01:09 +0200 Subject: Move the HZ macro to the kern/thread module --- arch/x86/machine/lapic.c | 4 ++-- arch/x86/machine/param.h | 8 -------- kern/thread.c | 8 ++++---- kern/thread.h | 8 ++++++++ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index c20eec7..e513688 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -212,7 +212,7 @@ lapic_compute_freq(void) lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY); log_info("lapic: bus frequency: %u.%02u MHz", lapic_bus_freq / 1000000, lapic_bus_freq % 1000000); - lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / THREAD_TICK_FREQ); lapic_write(&lapic_map->svr, 0); } @@ -239,7 +239,7 @@ lapic_setup_registers(void) lapic_write(&lapic_map->lvt_lint1, LAPIC_LVT_MASK_INTR); lapic_write(&lapic_map->lvt_error, TRAP_LAPIC_ERROR); lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1); - lapic_write(&lapic_map->timer_icr, lapic_bus_freq / HZ); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / THREAD_TICK_FREQ); } void __init diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index f9abaa4..db60c8f 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -24,14 +24,6 @@ #include -/* - * System timer frequency. - * - * The selected value of 200 translates to a period of 5ms, small enough to - * provide low latency, and is practical as both a dividend and divisor. - */ -#define HZ 200 - /* * 4 KiB pages. */ diff --git a/kern/thread.c b/kern/thread.c index 785640c..cd0717c 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -54,7 +54,7 @@ * * A few terms are used by both papers with slightly different meanings. Here * are the definitions used in this implementation : - * - The time unit is the system timer period (1 / HZ) + * - The time unit is the system timer period (1 / tick frequency) * - Work is the amount of execution time units consumed * - Weight is the amount of execution time units allocated * - A round is the shortest period during which all threads in a run queue @@ -159,7 +159,7 @@ /* * Default time slice for real-time round-robin scheduling. */ -#define THREAD_DEFAULT_RR_TIME_SLICE (HZ / 10) +#define THREAD_DEFAULT_RR_TIME_SLICE (THREAD_TICK_FREQ / 10) /* * Maximum number of threads which can be pulled from a remote run queue @@ -170,7 +170,7 @@ /* * Delay (in ticks) between two balance attempts when a run queue is idle. */ -#define THREAD_IDLE_BALANCE_TICKS (HZ / 2) +#define THREAD_IDLE_BALANCE_TICKS (THREAD_TICK_FREQ / 2) /* * Run queue properties for real-time threads. @@ -190,7 +190,7 @@ struct thread_rt_runq { /* * Round slice base unit for fair-scheduling threads. */ -#define THREAD_FS_ROUND_SLICE_BASE (HZ / 10) +#define THREAD_FS_ROUND_SLICE_BASE (THREAD_TICK_FREQ / 10) /* * Group of threads sharing the same weight. diff --git a/kern/thread.h b/kern/thread.h index f41daef..24e3c67 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -46,6 +46,14 @@ #include #include +/* + * Scheduler tick frequency. + * + * The selected value of 200 translates to a period of 5ms, small enough to + * provide low latency, and is practical as both a dividend and divisor. + */ +#define THREAD_TICK_FREQ 200 + /* * Thread structure. */ -- cgit v1.2.3 From 58401aa087399e0344b73e91f5708af2451b33e7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 22:12:09 +0200 Subject: Move the INTR_TABLE_SIZE macro to the x86/trap module --- arch/x86/machine/param.h | 5 ----- arch/x86/machine/trap.h | 2 ++ kern/intr.c | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index db60c8f..42133ab 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -31,11 +31,6 @@ #define PAGE_SIZE (1 << PAGE_SHIFT) #define PAGE_MASK (PAGE_SIZE - 1) -/* - * Maximum number of available interrupts. - */ -#define INTR_TABLE_SIZE 256 - /* * Virtual memory properties. */ diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index c2885c5..94639c0 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -65,6 +65,8 @@ #define TRAP_NR_VECTORS 256 +#define TRAP_INTR_TABLE_SIZE 256 + #define TRAP_STACK_SIZE PAGE_SIZE #ifndef __ASSEMBLER__ diff --git a/kern/intr.c b/kern/intr.c index ca733e2..6590fca 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -33,10 +33,10 @@ #include #include #include -#include #include #include #include +#include struct intr_handler { struct list node; @@ -76,7 +76,7 @@ struct intr_entry { /* * Interrupt table. */ -static struct intr_entry intr_table[INTR_TABLE_SIZE]; +static struct intr_entry intr_table[TRAP_INTR_TABLE_SIZE]; /* * List of registered controllers. -- cgit v1.2.3 From 9fb3131dbde508526dcbec29aac166ca425cd352 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 24 Jun 2017 22:49:22 +0200 Subject: Move physical memory properties into the new x86/pmem module --- arch/x86/Makefrag.am | 1 + arch/x86/machine/biosmem.c | 49 ++++++++++++++++----------------- arch/x86/machine/param.h | 37 ------------------------- arch/x86/machine/pmem.h | 67 ++++++++++++++++++++++++++++++++++++++++++++++ vm/vm_page.c | 22 +++++++-------- vm/vm_page.h | 3 ++- 6 files changed, 106 insertions(+), 73 deletions(-) create mode 100644 arch/x86/machine/pmem.h diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 965bd0b..964d133 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -57,6 +57,7 @@ x15_SOURCES += \ arch/x86/machine/pit.h \ arch/x86/machine/pmap.c \ arch/x86/machine/pmap.h \ + arch/x86/machine/pmem.h \ arch/x86/machine/ssp.c \ arch/x86/machine/ssp.h \ arch/x86/machine/strace.c \ diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 8be00b1..5d51059 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,7 @@ struct biosmem_zone { /* * Physical zone boundaries. */ -static struct biosmem_zone biosmem_zones[VM_PAGE_MAX_ZONES] __bootdata; +static struct biosmem_zone biosmem_zones[PMEM_MAX_ZONES] __bootdata; /* * Boundaries of the simple bootstrap heap. @@ -606,8 +607,8 @@ biosmem_setup_allocator(const struct multiboot_raw_info *mbi) end = vm_page_trunc((mbi->mem_upper + 1024) << 10); #ifndef __LP64__ - if (end > VM_PAGE_DIRECTMAP_LIMIT) { - end = VM_PAGE_DIRECTMAP_LIMIT; + if (end > PMEM_DIRECTMAP_LIMIT) { + end = PMEM_DIRECTMAP_LIMIT; } #endif /* __LP64__ */ @@ -661,46 +662,46 @@ biosmem_bootstrap(const struct multiboot_raw_info *mbi) biosmem_map_adjust(); phys_start = BIOSMEM_BASE; - phys_end = VM_PAGE_DMA_LIMIT; + phys_end = PMEM_DMA_LIMIT; error = biosmem_map_find_avail(&phys_start, &phys_end); if (error) { boot_panic(biosmem_panic_nozone_msg); } - biosmem_set_zone(VM_PAGE_ZONE_DMA, phys_start, phys_end); + biosmem_set_zone(PMEM_ZONE_DMA, phys_start, phys_end); - phys_start = VM_PAGE_DMA_LIMIT; -#ifdef VM_PAGE_DMA32_LIMIT - phys_end = VM_PAGE_DMA32_LIMIT; + phys_start = PMEM_DMA_LIMIT; +#ifdef PMEM_DMA32_LIMIT + phys_end = PMEM_DMA32_LIMIT; error = biosmem_map_find_avail(&phys_start, &phys_end); if (error) { goto out; } - biosmem_set_zone(VM_PAGE_ZONE_DMA32, phys_start, phys_end); + biosmem_set_zone(PMEM_ZONE_DMA32, phys_start, phys_end); - phys_start = VM_PAGE_DMA32_LIMIT; -#endif /* VM_PAGE_DMA32_LIMIT */ - phys_end = VM_PAGE_DIRECTMAP_LIMIT; + phys_start = PMEM_DMA32_LIMIT; +#endif /* PMEM_DMA32_LIMIT */ + phys_end = PMEM_DIRECTMAP_LIMIT; error = biosmem_map_find_avail(&phys_start, &phys_end); if (error) { goto out; } - biosmem_set_zone(VM_PAGE_ZONE_DIRECTMAP, phys_start, phys_end); + biosmem_set_zone(PMEM_ZONE_DIRECTMAP, phys_start, phys_end); - phys_start = VM_PAGE_DIRECTMAP_LIMIT; - phys_end = VM_PAGE_HIGHMEM_LIMIT; + phys_start = PMEM_DIRECTMAP_LIMIT; + phys_end = PMEM_HIGHMEM_LIMIT; error = biosmem_map_find_avail(&phys_start, &phys_end); if (error) { goto out; } - biosmem_set_zone(VM_PAGE_ZONE_HIGHMEM, phys_start, phys_end); + biosmem_set_zone(PMEM_ZONE_HIGHMEM, phys_start, phys_end); out: biosmem_setup_allocator(mbi); @@ -750,12 +751,12 @@ biosmem_get_bda(void) phys_addr_t __boot biosmem_directmap_end(void) { - if (biosmem_zone_size(VM_PAGE_ZONE_DIRECTMAP) != 0) { - return biosmem_zone_end(VM_PAGE_ZONE_DIRECTMAP); - } else if (biosmem_zone_size(VM_PAGE_ZONE_DMA32) != 0) { - return biosmem_zone_end(VM_PAGE_ZONE_DMA32); + if (biosmem_zone_size(PMEM_ZONE_DIRECTMAP) != 0) { + return biosmem_zone_end(PMEM_ZONE_DIRECTMAP); + } else if (biosmem_zone_size(PMEM_ZONE_DMA32) != 0) { + return biosmem_zone_end(PMEM_ZONE_DMA32); } else { - return biosmem_zone_end(VM_PAGE_ZONE_DMA); + return biosmem_zone_end(PMEM_ZONE_DMA); } } @@ -941,14 +942,14 @@ biosmem_free_usable(void) start = vm_page_round(entry->base_addr); - if (start >= VM_PAGE_HIGHMEM_LIMIT) { + if (start >= PMEM_HIGHMEM_LIMIT) { break; } end = vm_page_trunc(entry->base_addr + entry->length); - if (end > VM_PAGE_HIGHMEM_LIMIT) { - end = VM_PAGE_HIGHMEM_LIMIT; + if (end > PMEM_HIGHMEM_LIMIT) { + end = PMEM_HIGHMEM_LIMIT; } if (start < BIOSMEM_BASE) { diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h index 42133ab..7f1fc2e 100644 --- a/arch/x86/machine/param.h +++ b/arch/x86/machine/param.h @@ -96,41 +96,4 @@ #define VM_MAX_KMEM_ADDRESS VM_MAX_KERNEL_ADDRESS #endif /* __LP64__ */ -/* - * Physical memory properties. - */ - -#define VM_PAGE_DMA_LIMIT DECL_CONST(0x1000000, UL) - -#ifdef __LP64__ -#define VM_PAGE_MAX_ZONES 4 -#define VM_PAGE_DMA32_LIMIT DECL_CONST(0x100000000, UL) -#define VM_PAGE_DIRECTMAP_LIMIT DECL_CONST(0x400000000000, UL) -#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, UL) -#else /* __LP64__ */ -#define VM_PAGE_DIRECTMAP_LIMIT DECL_CONST(0x38000000, ULL) -#ifdef X15_X86_PAE -#define VM_PAGE_MAX_ZONES 3 -#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, ULL) -#else /* X15_X86_PAE */ -#define VM_PAGE_MAX_ZONES 3 -#define VM_PAGE_HIGHMEM_LIMIT DECL_CONST(0xfffff000, UL) -#endif /* X15_X86_PAE */ -#endif /* __LP64__ */ - -/* - * Physical zone indexes. - */ -#define VM_PAGE_ZONE_DMA 0 - -#ifdef __LP64__ -#define VM_PAGE_ZONE_DMA32 1 -#define VM_PAGE_ZONE_DIRECTMAP 2 -#define VM_PAGE_ZONE_HIGHMEM 3 -#else /* __LP64__ */ -#define VM_PAGE_ZONE_DMA32 1 /* Alias for the DIRECTMAP zone */ -#define VM_PAGE_ZONE_DIRECTMAP 1 -#define VM_PAGE_ZONE_HIGHMEM 2 -#endif /* __LP64__ */ - #endif /* _X86_PARAM_H */ diff --git a/arch/x86/machine/pmem.h b/arch/x86/machine/pmem.h new file mode 100644 index 0000000..b6c6db0 --- /dev/null +++ b/arch/x86/machine/pmem.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Physical memory layout. + * + * This file is a top header in the inclusion hierarchy, and shouldn't include + * other headers that may cause circular dependencies. + */ + +#ifndef _X86_PMEM_H +#define _X86_PMEM_H + +#include + +/* + * Zone boundaries. + */ + +#define PMEM_DMA_LIMIT DECL_CONST(0x1000000, UL) + +#ifdef __LP64__ +#define PMEM_MAX_ZONES 4 +#define PMEM_DMA32_LIMIT DECL_CONST(0x100000000, UL) +#define PMEM_DIRECTMAP_LIMIT DECL_CONST(0x400000000000, UL) +#define PMEM_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, UL) +#else /* __LP64__ */ +#define PMEM_DIRECTMAP_LIMIT DECL_CONST(0x38000000, ULL) +#ifdef X15_X86_PAE +#define PMEM_MAX_ZONES 3 +#define PMEM_HIGHMEM_LIMIT DECL_CONST(0x10000000000000, ULL) +#else /* X15_X86_PAE */ +#define PMEM_MAX_ZONES 3 +#define PMEM_HIGHMEM_LIMIT DECL_CONST(0xfffff000, UL) +#endif /* X15_X86_PAE */ +#endif /* __LP64__ */ + +/* + * Zone vm_page indexes. + */ + +#define PMEM_ZONE_DMA 0 +#define PMEM_ZONE_DMA32 1 + +#ifdef __LP64__ +#define PMEM_ZONE_DIRECTMAP 2 +#define PMEM_ZONE_HIGHMEM 3 +#else /* __LP64__ */ +#define PMEM_ZONE_DMA32 1 +#define PMEM_ZONE_DIRECTMAP 1 /* Alias for the DMA32 zone */ +#define PMEM_ZONE_HIGHMEM 2 +#endif /* __LP64__ */ + +#endif /* _X86_PMEM_H */ diff --git a/vm/vm_page.c b/vm/vm_page.c index 4ddf34a..2c48a0d 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include #include #include @@ -145,12 +145,12 @@ static int vm_page_is_ready __read_mostly; * the direct physical mapping, DMA and DMA32 are aliases for DIRECTMAP, * in which case the zone table contains DIRECTMAP and HIGHMEM only. */ -static struct vm_page_zone vm_page_zones[VM_PAGE_MAX_ZONES]; +static struct vm_page_zone vm_page_zones[PMEM_MAX_ZONES]; /* * Bootstrap zone table. */ -static struct vm_page_boot_zone vm_page_boot_zones[VM_PAGE_MAX_ZONES] +static struct vm_page_boot_zone vm_page_boot_zones[PMEM_MAX_ZONES] __initdata; /* @@ -552,16 +552,16 @@ vm_page_select_alloc_zone(unsigned int selector) switch (selector) { case VM_PAGE_SEL_DMA: - zone_index = VM_PAGE_ZONE_DMA; + zone_index = PMEM_ZONE_DMA; break; case VM_PAGE_SEL_DMA32: - zone_index = VM_PAGE_ZONE_DMA32; + zone_index = PMEM_ZONE_DMA32; break; case VM_PAGE_SEL_DIRECTMAP: - zone_index = VM_PAGE_ZONE_DIRECTMAP; + zone_index = PMEM_ZONE_DIRECTMAP; break; case VM_PAGE_SEL_HIGHMEM: - zone_index = VM_PAGE_ZONE_HIGHMEM; + zone_index = PMEM_ZONE_HIGHMEM; break; default: panic("vm_page: invalid selector"); @@ -795,13 +795,13 @@ const char * vm_page_zone_name(unsigned int zone_index) { /* Don't use a switch statement since zones can be aliased */ - if (zone_index == VM_PAGE_ZONE_HIGHMEM) { + if (zone_index == PMEM_ZONE_HIGHMEM) { return "HIGHMEM"; - } else if (zone_index == VM_PAGE_ZONE_DIRECTMAP) { + } else if (zone_index == PMEM_ZONE_DIRECTMAP) { return "DIRECTMAP"; - } else if (zone_index == VM_PAGE_ZONE_DMA32) { + } else if (zone_index == PMEM_ZONE_DMA32) { return "DMA32"; - } else if (zone_index == VM_PAGE_ZONE_DMA) { + } else if (zone_index == PMEM_ZONE_DMA) { return "DMA"; } else { panic("vm_page: invalid zone index"); diff --git a/vm/vm_page.h b/vm/vm_page.h index 34ad163..86ab527 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -30,6 +30,7 @@ #include #include #include +#include #include /* @@ -105,7 +106,7 @@ vm_page_to_pa(const struct vm_page *page) static inline uintptr_t vm_page_direct_va(phys_addr_t pa) { - assert(pa < VM_PAGE_DIRECTMAP_LIMIT); + assert(pa < PMEM_DIRECTMAP_LIMIT); return ((uintptr_t)pa + VM_MIN_DIRECTMAP_ADDRESS); } -- cgit v1.2.3 From 9e1c5185ee157f4b0b155c0a788bcd0557c44623 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 00:16:26 +0200 Subject: Remove the param module Move the page properties into the new x86/page module, and the virtual memory layout macros into the x86/pmap module. --- Makefrag.am | 1 - arch/x86/Makefrag.am | 2 +- arch/x86/machine/biosmem.c | 3 +- arch/x86/machine/boot.c | 2 +- arch/x86/machine/boot.h | 7 ++-- arch/x86/machine/boot_asm.S | 2 +- arch/x86/machine/cga.c | 1 - arch/x86/machine/cpu.c | 2 +- arch/x86/machine/lapic.c | 1 - arch/x86/machine/page.h | 29 +++++++++++++ arch/x86/machine/param.h | 99 --------------------------------------------- arch/x86/machine/pmap.c | 35 ++++++++-------- arch/x86/machine/pmap.h | 66 +++++++++++++++++++++++++++++- arch/x86/machine/tcb.h | 2 +- arch/x86/machine/trap.c | 1 - arch/x86/machine/trap.h | 5 ++- arch/x86/x15.lds.S | 5 ++- doc/intro.9.txt | 2 - doc/style.9.txt | 6 --- kern/kmem.c | 2 +- kern/macros.h | 3 ++ kern/param.h | 23 ----------- kern/sref.c | 1 - kern/thread.c | 2 +- test/test_llsync_defer.c | 2 +- test/test_pmap_update_mp.c | 2 +- test/test_vm_page_fill.c | 1 + vm/vm_kmem.c | 2 +- vm/vm_kmem.h | 6 +-- vm/vm_map.c | 6 +-- vm/vm_page.c | 2 +- vm/vm_page.h | 10 ++--- 32 files changed, 151 insertions(+), 182 deletions(-) create mode 100644 arch/x86/machine/page.h delete mode 100644 arch/x86/machine/param.h delete mode 100644 kern/param.h diff --git a/Makefrag.am b/Makefrag.am index 1607f3c..886ec1f 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -54,7 +54,6 @@ x15_SOURCES += \ kern/mutex_types.h \ kern/panic.c \ kern/panic.h \ - kern/param.h \ kern/percpu.c \ kern/percpu.h \ kern/plist.c \ diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index 964d133..cf53698 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -50,7 +50,7 @@ x15_SOURCES += \ arch/x86/machine/lapic.c \ arch/x86/machine/lapic.h \ arch/x86/machine/multiboot.h \ - arch/x86/machine/param.h \ + arch/x86/machine/page.h \ arch/x86/machine/pic.c \ arch/x86/machine/pic.h \ arch/x86/machine/pit.c \ diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 5d51059..64482c6 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -25,11 +25,12 @@ #include #include #include -#include #include #include #include #include +#include +#include #include #include #include diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index df7a206..63fcaf0 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -70,6 +69,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/machine/boot.h b/arch/x86/machine/boot.h index c207059..7a4d85d 100644 --- a/arch/x86/machine/boot.h +++ b/arch/x86/machine/boot.h @@ -19,7 +19,8 @@ #define _X86_BOOT_H #include -#include +#include +#include /* * Size of the stack used when booting a processor. @@ -36,7 +37,7 @@ /* * The kernel is physically loaded at BOOT_OFFSET by the boot loader. It * is divided in two parts: the .boot section which uses physical addresses - * and the main kernel code and data at VM_KERNEL_OFFSET. + * and the main kernel code and data at PMAP_KERNEL_OFFSET. * * See the linker script for more information. */ @@ -45,7 +46,7 @@ /* * Virtual to physical address translation macro. */ -#define BOOT_VTOP(addr) ((addr) - VM_KERNEL_OFFSET) +#define BOOT_VTOP(addr) ((addr) - PMAP_KERNEL_OFFSET) /* * Address where the MP trampoline code is copied and run at. diff --git a/arch/x86/machine/boot_asm.S b/arch/x86/machine/boot_asm.S index 4393c13..0aaf342 100644 --- a/arch/x86/machine/boot_asm.S +++ b/arch/x86/machine/boot_asm.S @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include /* diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index b59c40f..3ce4032 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 6baff62..6263e18 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index e513688..a0eee85 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/page.h b/arch/x86/machine/page.h new file mode 100644 index 0000000..43ab237 --- /dev/null +++ b/arch/x86/machine/page.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * This file is a top header in the inclusion hierarchy, and shouldn't include + * other headers that may cause circular dependencies. + */ + +#ifndef _X86_PAGE_H +#define _X86_PAGE_H + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) + +#endif /* _X86_PAGE_H */ diff --git a/arch/x86/machine/param.h b/arch/x86/machine/param.h deleted file mode 100644 index 7f1fc2e..0000000 --- a/arch/x86/machine/param.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 3 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 . - * - * - * This file is a top header in the inclusion hierarchy, and shouldn't include - * other headers that may cause circular dependencies. - */ - -#ifndef _X86_PARAM_H -#define _X86_PARAM_H - -#include - -/* - * 4 KiB pages. - */ -#define PAGE_SHIFT 12 -#define PAGE_SIZE (1 << PAGE_SHIFT) -#define PAGE_MASK (PAGE_SIZE - 1) - -/* - * Virtual memory properties. - */ - -/* - * User space boundaries. - */ -#define VM_MIN_ADDRESS DECL_CONST(0, UL) - -#ifdef __LP64__ -#define VM_MAX_ADDRESS DECL_CONST(0x800000000000, UL) -#else /* __LP64__ */ -#define VM_MAX_ADDRESS DECL_CONST(0xc0000000, UL) -#endif/* __LP64__ */ - -/* - * Kernel space boundaries. - */ -#ifdef __LP64__ -#define VM_MIN_KERNEL_ADDRESS DECL_CONST(0xffff800000000000, UL) -#define VM_MAX_KERNEL_ADDRESS DECL_CONST(0xfffffffffffff000, UL) -#else /* __LP64__ */ -#define VM_MIN_KERNEL_ADDRESS VM_MAX_ADDRESS -#define VM_MAX_KERNEL_ADDRESS DECL_CONST(0xfffff000, UL) -#endif /* __LP64__ */ - -/* - * Direct physical mapping boundaries. - */ -#ifdef __LP64__ -#define VM_MIN_DIRECTMAP_ADDRESS VM_MIN_KERNEL_ADDRESS -#define VM_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xffffc00000000000, UL) -#else /* __LP64__ */ -#define VM_MIN_DIRECTMAP_ADDRESS VM_MAX_ADDRESS -#define VM_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xf8000000, UL) -#endif /* __LP64__ */ - -/* - * Kernel mapping offset. - * - * On 32-bits systems, the kernel is linked at addresses included in the - * direct physical mapping, whereas on 64-bits systems, it is linked at - * -2 GiB because the "kernel" memory model is used when compiling (see - * the -mcmodel=kernel gcc option). - */ -#ifdef __LP64__ -#define VM_KERNEL_OFFSET DECL_CONST(0xffffffff80000000, UL) -#else /* __LP64__ */ -#define VM_KERNEL_OFFSET VM_MIN_DIRECTMAP_ADDRESS -#endif /* __LP64__ */ - -/* - * Kernel virtual space boundaries. - * - * In addition to the direct physical mapping, the kernel has its own virtual - * memory space. - */ -#define VM_MIN_KMEM_ADDRESS VM_MAX_DIRECTMAP_ADDRESS - -#ifdef __LP64__ -#define VM_MAX_KMEM_ADDRESS VM_KERNEL_OFFSET -#else /* __LP64__ */ -#define VM_MAX_KMEM_ADDRESS VM_MAX_KERNEL_ADDRESS -#endif /* __LP64__ */ - -#endif /* _X86_PARAM_H */ diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 0520c13..628a9d9 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -40,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -447,11 +447,12 @@ pmap_setup_paging(void) directmap_end = biosmem_directmap_end(); - if (directmap_end > (VM_MAX_DIRECTMAP_ADDRESS - VM_MIN_DIRECTMAP_ADDRESS)) { + if (directmap_end > (PMAP_MAX_DIRECTMAP_ADDRESS + - PMAP_MIN_DIRECTMAP_ADDRESS)) { boot_panic(pmap_panic_directmap_msg); } - va = VM_MIN_DIRECTMAP_ADDRESS; + va = PMAP_MIN_DIRECTMAP_ADDRESS; pa = 0; for (i = 0; i < directmap_end; i += pgsize) { @@ -464,7 +465,7 @@ pmap_setup_paging(void) /* * On 64-bits systems, the kernel isn't linked at addresses included * in the direct mapping, which requires the creation of an additional - * mapping for it. See param.h for more details. + * mapping for it. */ va = P2ALIGN((uintptr_t)&_init, pgsize); pa = BOOT_VTOP(va); @@ -509,14 +510,14 @@ pmap_ap_setup_paging(void) #define pmap_assert_range(pmap, start, end) \ MACRO_BEGIN \ assert((start) < (end)); \ - assert(((end) <= VM_MIN_DIRECTMAP_ADDRESS) \ - || ((start) >= VM_MAX_DIRECTMAP_ADDRESS)); \ + assert(((end) <= PMAP_MIN_DIRECTMAP_ADDRESS) \ + || ((start) >= PMAP_MAX_DIRECTMAP_ADDRESS)); \ \ if ((pmap) == kernel_pmap) { \ - assert(((start) >= VM_MIN_KMEM_ADDRESS) \ - && ((end) <= VM_MAX_KMEM_ADDRESS)); \ + assert(((start) >= PMAP_MIN_KMEM_ADDRESS) \ + && ((end) <= PMAP_MAX_KMEM_ADDRESS)); \ } else { \ - assert((end) <= VM_MAX_ADDRESS); \ + assert((end) <= PMAP_MAX_ADDRESS); \ } \ MACRO_END @@ -583,7 +584,7 @@ pmap_walk_vas(uintptr_t start, uintptr_t end, pmap_walk_fn_t walk_fn) assert(vm_page_aligned(start)); assert(start < end); #ifdef __LP64__ - assert((start < VM_MAX_ADDRESS) || (start >= VM_MIN_KERNEL_ADDRESS)); + assert((start < PMAP_MAX_ADDRESS) || (start >= PMAP_MIN_KERNEL_ADDRESS)); #endif /* __LP64__ */ va = start; @@ -592,8 +593,8 @@ pmap_walk_vas(uintptr_t start, uintptr_t end, pmap_walk_fn_t walk_fn) do { #ifdef __LP64__ /* Handle long mode canonical form */ - if (va == VM_MAX_ADDRESS) { - va = VM_MIN_KERNEL_ADDRESS; + if (va == PMAP_MAX_ADDRESS) { + va = PMAP_MIN_KERNEL_ADDRESS; } #endif /* __LP64__ */ @@ -641,7 +642,7 @@ pmap_setup_global_page(phys_addr_t ptp_pa, unsigned int index, static void __init pmap_setup_global_pages(void) { - pmap_walk_vas(VM_MIN_KERNEL_ADDRESS, VM_MAX_KERNEL_ADDRESS, + pmap_walk_vas(PMAP_MIN_KERNEL_ADDRESS, PMAP_MAX_KERNEL_ADDRESS, pmap_setup_global_page); pmap_pt_levels[0].mask |= PMAP_PTE_G; cpu_enable_global_pages(); @@ -900,7 +901,7 @@ pmap_setup_set_ptp_type(phys_addr_t ptp_pa, unsigned int index, static void __init pmap_setup_fix_ptps(void) { - pmap_walk_vas(VM_MIN_ADDRESS, VM_MAX_KERNEL_ADDRESS, + pmap_walk_vas(PMAP_MIN_ADDRESS, PMAP_MAX_KERNEL_ADDRESS, pmap_setup_set_ptp_type); } @@ -946,8 +947,8 @@ pmap_copy_cpu_table_recursive(const pmap_pte_t *sptp, unsigned int level, i++, va = P2END(va, 1UL << pt_level->skip)) { #ifdef __LP64__ /* Handle long mode canonical form */ - if (va == VM_MAX_ADDRESS) { - va = VM_MIN_KERNEL_ADDRESS; + if (va == PMAP_MAX_ADDRESS) { + va = PMAP_MIN_KERNEL_ADDRESS; } #endif /* __LP64__ */ @@ -1006,7 +1007,7 @@ pmap_copy_cpu_table(unsigned int cpu) dptp = vm_page_direct_ptr(page); #endif /* X15_X86_PAE */ - pmap_copy_cpu_table_recursive(sptp, level, dptp, VM_MIN_ADDRESS); + pmap_copy_cpu_table_recursive(sptp, level, dptp, PMAP_MIN_ADDRESS); } void __init diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 9c76f5d..2b23126 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -23,6 +23,71 @@ #include +/* + * Virtual memory layout. + */ + +/* + * User space boundaries. + */ +#define PMAP_MIN_ADDRESS DECL_CONST(0, UL) + +#ifdef __LP64__ +#define PMAP_MAX_ADDRESS DECL_CONST(0x800000000000, UL) +#else /* __LP64__ */ +#define PMAP_MAX_ADDRESS DECL_CONST(0xc0000000, UL) +#endif/* __LP64__ */ + +/* + * Kernel space boundaries. + */ +#ifdef __LP64__ +#define PMAP_MIN_KERNEL_ADDRESS DECL_CONST(0xffff800000000000, UL) +#define PMAP_MAX_KERNEL_ADDRESS DECL_CONST(0xfffffffffffff000, UL) +#else /* __LP64__ */ +#define PMAP_MIN_KERNEL_ADDRESS PMAP_MAX_ADDRESS +#define PMAP_MAX_KERNEL_ADDRESS DECL_CONST(0xfffff000, UL) +#endif /* __LP64__ */ + +/* + * Direct physical mapping boundaries. + */ +#ifdef __LP64__ +#define PMAP_MIN_DIRECTMAP_ADDRESS PMAP_MIN_KERNEL_ADDRESS +#define PMAP_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xffffc00000000000, UL) +#else /* __LP64__ */ +#define PMAP_MIN_DIRECTMAP_ADDRESS PMAP_MAX_ADDRESS +#define PMAP_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xf8000000, UL) +#endif /* __LP64__ */ + +/* + * Kernel mapping offset. + * + * On 32-bits systems, the kernel is linked at addresses included in the + * direct physical mapping, whereas on 64-bits systems, it is linked at + * -2 GiB because the "kernel" memory model is used when compiling (see + * the -mcmodel=kernel gcc option). + */ +#ifdef __LP64__ +#define PMAP_KERNEL_OFFSET DECL_CONST(0xffffffff80000000, UL) +#else /* __LP64__ */ +#define PMAP_KERNEL_OFFSET PMAP_MIN_DIRECTMAP_ADDRESS +#endif /* __LP64__ */ + +/* + * Kernel virtual space boundaries. + * + * In addition to the direct physical mapping, the kernel has its own virtual + * memory space. + */ +#define PMAP_MIN_KMEM_ADDRESS PMAP_MAX_DIRECTMAP_ADDRESS + +#ifdef __LP64__ +#define PMAP_MAX_KMEM_ADDRESS PMAP_KERNEL_OFFSET +#else /* __LP64__ */ +#define PMAP_MAX_KMEM_ADDRESS PMAP_MAX_KERNEL_ADDRESS +#endif /* __LP64__ */ + /* * Page table entry flags. */ @@ -103,7 +168,6 @@ #include #include #include -#include #include /* diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 0bb900c..b5b20bf 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -25,8 +25,8 @@ #include #include -#include #include +#include /* * Thread stack size. diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 5950231..19ddaf7 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index 94639c0..646eda3 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -16,12 +16,15 @@ * * * Trap (interrupt and exception) handling. + * + * This file is a top header in the inclusion hierarchy, and shouldn't include + * other headers that may cause circular dependencies. */ #ifndef _X86_TRAP_H #define _X86_TRAP_H -#include +#include /* * Architecture defined traps. diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index b5400c4..0bf809b 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -10,7 +10,8 @@ ENTRY(_start) #include #include -#include +#include +#include PHDRS { @@ -37,7 +38,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); _boot_end = .; - . += VM_KERNEL_OFFSET; + . += PMAP_KERNEL_OFFSET; _init = .; .init ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.init))) { diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 37691a1..e3eb4ee 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -219,8 +219,6 @@ module:arch/atomic:: Architecture-specific support for atomic instructions. module:arch/cpu:: Processor interface. -module:arch/param:: - Miscellaneous parameters. module:arch/pmap:: Physical mappings, the MMU driver. module:arch/strace:: diff --git a/doc/style.9.txt b/doc/style.9.txt index 1f8b23e..51ff043 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -387,12 +387,6 @@ kmem_cache_alloc(struct kmem_cache *cache) void kmem_info(void); -------------------------------------------------------------------------------- -There is currently one exception to the naming rules, which is the -module:arch/param module. The only API changes allowed on this module -are removals, in order to move certain symbols where they should be, -and renames using the param namespace, for symbols that actually belong -to the module. - FUNCTIONS --------- diff --git a/kern/kmem.c b/kern/kmem.c index 9055731..572df4f 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -57,10 +57,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/kern/macros.h b/kern/macros.h index 63ded05..743bdc0 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -16,6 +16,9 @@ * * * Helper macros. + * + * This file is a top header in the inclusion hierarchy, and shouldn't include + * other headers that may cause circular dependencies. */ #ifndef _KERN_MACROS_H diff --git a/kern/param.h b/kern/param.h deleted file mode 100644 index d006172..0000000 --- a/kern/param.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2012 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 3 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 _KERN_PARAM_H -#define _KERN_PARAM_H - -#include - -#endif /* _KERN_PARAM_H */ diff --git a/kern/sref.c b/kern/sref.c index bc3f9e9..7c4f14e 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include diff --git a/kern/thread.c b/kern/thread.c index cd0717c..eb0f1eb 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -99,7 +99,6 @@ #include #include #include -#include #include #include #include @@ -111,6 +110,7 @@ #include #include #include +#include #include #include #include diff --git a/test/test_llsync_defer.c b/test/test_llsync_defer.c index 2674799..249c53a 100644 --- a/test/test_llsync_defer.c +++ b/test/test_llsync_defer.c @@ -39,9 +39,9 @@ #include #include #include -#include #include #include +#include #include #include diff --git a/test/test_pmap_update_mp.c b/test/test_pmap_update_mp.c index 5588ce8..c058323 100644 --- a/test/test_pmap_update_mp.c +++ b/test/test_pmap_update_mp.c @@ -33,8 +33,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/test/test_vm_page_fill.c b/test/test_vm_page_fill.c index fe35103..2b7f113 100644 --- a/test/test_vm_page_fill.c +++ b/test/test_vm_page_fill.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 8139ddb..488ae30 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index 30c7cd3..3628a69 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -20,16 +20,16 @@ #include -#include +#include #include /* * The kernel space is required not to start at address 0, which is used to * report allocation errors. */ -#if VM_MIN_KMEM_ADDRESS == 0 +#if PMAP_MIN_KMEM_ADDRESS == 0 #error "kernel space must not start at address 0" -#endif /* VM_MIN_KMEM_ADDRESS == 0 */ +#endif /* PMAP_MIN_KMEM_ADDRESS == 0 */ /* * Special kernel addresses. diff --git a/vm/vm_map.c b/vm/vm_map.c index abe2aa8..389678b 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -31,10 +31,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -734,7 +734,7 @@ void __init vm_map_setup(void) { vm_map_init(kernel_map, kernel_pmap, - VM_MIN_KMEM_ADDRESS, VM_MAX_KMEM_ADDRESS); + PMAP_MIN_KMEM_ADDRESS, PMAP_MAX_KMEM_ADDRESS); kmem_cache_init(&vm_map_entry_cache, "vm_map_entry", sizeof(struct vm_map_entry), 0, NULL, KMEM_CACHE_PAGE_ONLY); @@ -763,7 +763,7 @@ vm_map_create(struct vm_map **mapp) goto error_pmap; } - vm_map_init(map, pmap, VM_MIN_ADDRESS, VM_MAX_ADDRESS); + vm_map_init(map, pmap, PMAP_MIN_ADDRESS, PMAP_MAX_ADDRESS); *mapp = map; return 0; diff --git a/vm/vm_page.c b/vm/vm_page.c index 2c48a0d..caa8c98 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -41,10 +41,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/vm/vm_page.h b/vm/vm_page.h index 86ab527..026c118 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -107,15 +107,15 @@ static inline uintptr_t vm_page_direct_va(phys_addr_t pa) { assert(pa < PMEM_DIRECTMAP_LIMIT); - return ((uintptr_t)pa + VM_MIN_DIRECTMAP_ADDRESS); + return ((uintptr_t)pa + PMAP_MIN_DIRECTMAP_ADDRESS); } static inline phys_addr_t vm_page_direct_pa(uintptr_t va) { - assert(va >= VM_MIN_DIRECTMAP_ADDRESS); - assert(va < VM_MAX_DIRECTMAP_ADDRESS); - return (va - VM_MIN_DIRECTMAP_ADDRESS); + assert(va >= PMAP_MIN_DIRECTMAP_ADDRESS); + assert(va < PMAP_MAX_DIRECTMAP_ADDRESS); + return (va - PMAP_MIN_DIRECTMAP_ADDRESS); } static inline void * -- cgit v1.2.3 From cbb58c1daa596d10fa5d622f302e530fa42a02a1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 10:52:57 +0200 Subject: x86/cpu: remove unnecessary qualifiers on the IDT register variable --- arch/x86/machine/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 6263e18..4746c5f 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -414,7 +414,7 @@ cpu_idt_set_double_fault(void (*isr)(void)) static void cpu_load_idt(void) { - static volatile struct cpu_pseudo_desc idtr; /* TODO Review this */ + struct cpu_pseudo_desc idtr; idtr.address = (unsigned long)cpu_idt; idtr.limit = sizeof(cpu_idt) - 1; -- cgit v1.2.3 From 308dae7cb2708e48699700556b0a49483097f262 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 14:25:44 +0200 Subject: doc/style(9): discuss increment and decrement operators --- doc/style.9.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/style.9.txt b/doc/style.9.txt index 51ff043..93683d8 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -644,6 +644,17 @@ Side effects and conditions Conditions must never be made of expressions with side effects. This completely removes the need to think about short-circuit evaluations. +Increment/decrement operators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Increment and decrement operators may never be used inside larger expressions. +Their usage as expression-3 of a for loop is considered as an entire expression +and doesn't violate this rule. The rationale is to avoid tricky errors related +to sequence points. Since such operators may not be used inside larger +expressions, there is no difference between prefix and postfix operators. +The latter are preferred because they look more object-oriented, somewhat +matching the object.method() syntax. + SEE --- -- cgit v1.2.3 From 66b03bcbc989cfcbd46dcbee1c9ab7ace54f98f0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 14:25:59 +0200 Subject: Fix increment/decrement operator style mistakes --- arch/x86/machine/boot.c | 33 +++++++++++++++++++++----------- kern/fmt.c | 51 ++++++++++++++++++++++++++++++++++++++----------- kern/string.c | 22 +++++++++++++-------- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 63fcaf0..76fbcb4 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -137,7 +137,9 @@ boot_memcpy(void *dest, const void *src, size_t n) src_ptr = src; for (i = 0; i < n; i++) { - *dest_ptr++ = *src_ptr++; + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; } return dest; @@ -155,14 +157,18 @@ boot_memmove(void *dest, const void *src, size_t n) src_ptr = src; for (i = 0; i < n; i++) { - *dest_ptr++ = *src_ptr++; + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; } } else { dest_ptr = dest + n - 1; src_ptr = src + n - 1; for (i = 0; i < n; i++) { - *dest_ptr-- = *src_ptr--; + *dest_ptr = *src_ptr; + dest_ptr--; + src_ptr--; } } @@ -187,15 +193,15 @@ boot_memset(void *s, int c, size_t n) size_t __boot boot_strlen(const char *s) { - size_t i; + const char *start; - i = 0; + start = s; - while (*s++ != '\0') { - i++; + while (*s != '\0') { + s++; } - return i; + return (s - start); } void __boot @@ -210,17 +216,22 @@ boot_panic(const char *msg) s = boot_panic_intro_msg; while ((ptr < end) && (*s != '\0')) { - *ptr++ = (BOOT_CGACOLOR << 8) | *s++; + *ptr = (BOOT_CGACOLOR << 8) | *s; + ptr++; + s++; } s = msg; while ((ptr < end) && (*s != '\0')) { - *ptr++ = (BOOT_CGACOLOR << 8) | *s++; + *ptr = (BOOT_CGACOLOR << 8) | *s; + ptr++; + s++; } while (ptr < end) { - *ptr++ = (BOOT_CGACOLOR << 8) | ' '; + *ptr = (BOOT_CGACOLOR << 8) | ' '; + ptr++; } cpu_halt(); diff --git a/kern/fmt.c b/kern/fmt.c index 34424e2..57ea069 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -525,7 +525,8 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) if (n == 0) { if (state->precision != 0) { - tmp[i++] = '0'; + tmp[i] = '0'; + i++; } } else if (state->base == 10) { /* @@ -543,7 +544,8 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) do { r = n % 10; n /= 10; - tmp[i++] = fmt_digits[r]; + tmp[i] = fmt_digits[r]; + i++; } while (n != 0); #ifndef __LP64__ } else { @@ -554,7 +556,8 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) do { r = m % 10; m /= 10; - tmp[i++] = fmt_digits[r]; + tmp[i] = fmt_digits[r]; + i++; } while (m != 0); } #endif /* __LP64__ */ @@ -565,7 +568,8 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) do { r = n & mask; n >>= shift; - tmp[i++] = fmt_digits[r] | (state->flags & FMT_FORMAT_LOWER); + tmp[i] = fmt_digits[r] | (state->flags & FMT_FORMAT_LOWER); + i++; } while (n != 0); } @@ -576,9 +580,12 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) state->width -= state->precision; if (!(state->flags & (FMT_FORMAT_LEFT_JUSTIFY | FMT_FORMAT_ZERO_PAD))) { - while (state->width-- > 0) { + while (state->width > 0) { + state->width--; fmt_sprintf_state_produce_raw_char(state, ' '); } + + state->width--; } if (state->flags & FMT_FORMAT_ALT_FORM) { @@ -595,22 +602,32 @@ fmt_sprintf_state_produce_int(struct fmt_sprintf_state *state) if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { c = (state->flags & FMT_FORMAT_ZERO_PAD) ? '0' : ' '; - while (state->width-- > 0) { + while (state->width > 0) { + state->width--; fmt_sprintf_state_produce_raw_char(state, c); } + + state->width--; } - while (i < state->precision--) { + while (i < state->precision) { + state->precision--; fmt_sprintf_state_produce_raw_char(state, '0'); } - while (i-- > 0) { + state->precision--; + + while (i > 0) { + i--; fmt_sprintf_state_produce_raw_char(state, tmp[i]); } - while (state->width-- > 0) { + while (state->width > 0) { + state->width--; fmt_sprintf_state_produce_raw_char(state, ' '); } + + state->width--; } static void @@ -621,14 +638,26 @@ fmt_sprintf_state_produce_char(struct fmt_sprintf_state *state) c = va_arg(state->ap, int); if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { - while (--state->width > 0) { + for (;;) { + state->width--; + + if (state->width <= 0) { + break; + } + fmt_sprintf_state_produce_raw_char(state, ' '); } } fmt_sprintf_state_produce_raw_char(state, c); - while (--state->width > 0) { + for (;;) { + state->width--; + + if (state->width <= 0) { + break; + } + fmt_sprintf_state_produce_raw_char(state, ' '); } } diff --git a/kern/string.c b/kern/string.c index 763012a..7508e79 100644 --- a/kern/string.c +++ b/kern/string.c @@ -35,7 +35,9 @@ memcpy(void *dest, const void *src, size_t n) src_ptr = src; for (i = 0; i < n; i++) { - *dest_ptr++ = *src_ptr++; + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; } return dest; @@ -55,14 +57,18 @@ memmove(void *dest, const void *src, size_t n) src_ptr = src; for (i = 0; i < n; i++) { - *dest_ptr++ = *src_ptr++; + *dest_ptr = *src_ptr; + dest_ptr++; + src_ptr++; } } else { dest_ptr = dest + n - 1; src_ptr = src + n - 1; for (i = 0; i < n; i++) { - *dest_ptr-- = *src_ptr--; + *dest_ptr = *src_ptr; + dest_ptr--; + src_ptr--; } } @@ -110,15 +116,15 @@ memcmp(const void *s1, const void *s2, size_t n) size_t strlen(const char *s) { - size_t i; + const char *start; - i = 0; + start = s; - while (*s++ != '\0') { - i++; + while (*s != '\0') { + s++; } - return i; + return (s - start); } #endif /* STRING_ARCH_STRLEN */ -- cgit v1.2.3 From 5ee8402d3746e1e7831f6094aa07d29c5306b416 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 20:43:28 +0200 Subject: kern/{error,percpu}: include stddef.h --- kern/error.c | 2 ++ kern/percpu.h | 1 + 2 files changed, 3 insertions(+) diff --git a/kern/error.c b/kern/error.c index 110c804..f5d43e4 100644 --- a/kern/error.c +++ b/kern/error.c @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #include #include diff --git a/kern/percpu.h b/kern/percpu.h index d6a8d1a..18eb8aa 100644 --- a/kern/percpu.h +++ b/kern/percpu.h @@ -54,6 +54,7 @@ #define _KERN_PERCPU_H #include +#include #include #include -- cgit v1.2.3 From 9ded7917da06dea7f7284648281fd5d827f8a9a4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 20:44:44 +0200 Subject: Use the C11 alignas and noreturn macros --- arch/x86/machine/boot.c | 11 ++++++----- arch/x86/machine/boot.h | 4 +++- arch/x86/machine/cpu.c | 5 +++-- arch/x86/machine/cpu.h | 6 ++++-- arch/x86/machine/pmap.c | 17 +++++++++-------- arch/x86/machine/tcb.c | 7 ++++--- arch/x86/machine/tcb.h | 4 ++-- arch/x86/machine/trap.c | 3 ++- kern/intr.c | 9 +++++---- kern/kernel.h | 6 +++--- kern/kmem_i.h | 5 +++-- kern/llsync_i.h | 3 ++- kern/panic.h | 4 ++-- kern/sleepq.c | 5 +++-- kern/spinlock.c | 3 ++- kern/thread.c | 10 ++++++---- kern/thread.h | 6 +++--- kern/thread_i.h | 6 +++--- kern/turnstile.c | 5 +++-- kern/work.c | 5 +++-- kern/xcall.c | 9 +++++---- vm/vm_page.c | 5 +++-- 22 files changed, 79 insertions(+), 59 deletions(-) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 76fbcb4..7ce7b85 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -42,6 +42,7 @@ * to "enabling paging" do not refer to this initial identity mapping. */ +#include #include #include #include @@ -77,14 +78,14 @@ #include #include -char boot_stack[BOOT_STACK_SIZE] __aligned(CPU_DATA_ALIGN) __bootdata; -char boot_ap_stack[BOOT_STACK_SIZE] __aligned(CPU_DATA_ALIGN) __bootdata; +alignas(CPU_DATA_ALIGN) char boot_stack[BOOT_STACK_SIZE] __bootdata; +alignas(CPU_DATA_ALIGN) char boot_ap_stack[BOOT_STACK_SIZE] __bootdata; unsigned int boot_ap_id __bootdata; #ifdef __LP64__ -pmap_pte_t boot_pml4[PMAP_L3_PTES_PER_PT] __aligned(PAGE_SIZE) __bootdata; -pmap_pte_t boot_pdpt[PMAP_L2_PTES_PER_PT] __aligned(PAGE_SIZE) __bootdata; -pmap_pte_t boot_pdir[4 * PMAP_L1_PTES_PER_PT] __aligned(PAGE_SIZE) __bootdata; +alignas(PAGE_SIZE) pmap_pte_t boot_pml4[PMAP_L3_PTES_PER_PT] __bootdata; +alignas(PAGE_SIZE) pmap_pte_t boot_pdpt[PMAP_L2_PTES_PER_PT] __bootdata; +alignas(PAGE_SIZE) pmap_pte_t boot_pdir[4 * PMAP_L1_PTES_PER_PT] __bootdata; char boot_panic_long_mode_msg[] __bootdata = "boot: processor doesn't support long mode"; #endif /* __LP64__ */ diff --git a/arch/x86/machine/boot.h b/arch/x86/machine/boot.h index 7a4d85d..8c590c6 100644 --- a/arch/x86/machine/boot.h +++ b/arch/x86/machine/boot.h @@ -18,6 +18,8 @@ #ifndef _X86_BOOT_H #define _X86_BOOT_H +#include + #include #include #include @@ -99,7 +101,7 @@ void * boot_memcpy(void *dest, const void *src, size_t n); void * boot_memmove(void *dest, const void *src, size_t n); void * boot_memset(void *s, int c, size_t n); size_t boot_strlen(const char *s); -void __noreturn boot_panic(const char *s); +noreturn void boot_panic(const char *s); /* * This function is called by the bootstrap code before paging is enabled. diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 4746c5f..a53d8e2 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -135,7 +136,7 @@ static const struct cpu_tls_seg cpu_tls_seg = { /* * Interrupt descriptor table. */ -static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; +static alignas(8) struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __read_mostly; /* * Double fault handler, and stack for the main processor. @@ -144,7 +145,7 @@ static struct cpu_gate_desc cpu_idt[CPU_IDT_SIZE] __aligned(8) __read_mostly; * memory. */ static unsigned long cpu_double_fault_handler; -static char cpu_double_fault_stack[TRAP_STACK_SIZE] __aligned(CPU_DATA_ALIGN); +static alignas(CPU_DATA_ALIGN) char cpu_double_fault_stack[TRAP_STACK_SIZE]; void cpu_delay(unsigned long usecs) diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index 0061e13..ee4eb18 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -123,7 +123,9 @@ #ifndef __ASSEMBLER__ +#include #include +#include #include #include @@ -238,7 +240,7 @@ struct cpu { unsigned int features4; unsigned short phys_addr_width; unsigned short virt_addr_width; - char gdt[CPU_GDT_SIZE] __aligned(8); + alignas(8) char gdt[CPU_GDT_SIZE]; struct cpu_tss tss; #ifndef __LP64__ struct cpu_tss double_fault_tss; @@ -398,7 +400,7 @@ cpu_idle(void) * * Implies a compiler barrier. */ -static __noreturn __always_inline void +noreturn static __always_inline void cpu_halt(void) { cpu_intr_disable(); diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 628a9d9..93b6875 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -113,8 +114,8 @@ struct pmap *pmap_current_ptr __percpu; /* * "Hidden" kernel root page tables for PAE mode. */ -static pmap_pte_t pmap_cpu_kpdpts[X15_MAX_CPUS][PMAP_L2_PTES_PER_PT] - __read_mostly __aligned(PMAP_PDPT_ALIGN); +static alignas(PMAP_PDPT_ALIGN) pmap_pte_t + pmap_cpu_kpdpts[X15_MAX_CPUS][PMAP_L2_PTES_PER_PT] __read_mostly; #endif /* X15_X86_PAE */ @@ -184,11 +185,11 @@ struct pmap_update_op { * operation. */ struct pmap_update_oplist { - struct cpumap cpumap; + alignas(CPU_L1_SIZE) struct cpumap cpumap; struct pmap *pmap; unsigned int nr_ops; struct pmap_update_op ops[PMAP_UPDATE_MAX_OPS]; -} __aligned(CPU_L1_SIZE); +}; static unsigned int pmap_oplist_tsd_key __read_mostly; @@ -219,13 +220,13 @@ struct pmap_update_queue { * they perform. */ struct pmap_syncer { - struct thread *thread; + alignas(CPU_L1_SIZE) struct thread *thread; struct pmap_update_queue queue; struct syscnt sc_updates; struct syscnt sc_update_enters; struct syscnt sc_update_removes; struct syscnt sc_update_protects; -} __aligned(CPU_L1_SIZE); +}; static void pmap_sync(void *arg); @@ -244,14 +245,14 @@ static struct pmap_syncer pmap_syncer __percpu; * individual TLB entries or globally flush the TLB. */ struct pmap_update_request { - struct list node; + alignas(CPU_L1_SIZE) struct list node; struct spinlock lock; struct thread *sender; const struct pmap_update_oplist *oplist; unsigned int nr_mappings; int done; int error; -} __aligned(CPU_L1_SIZE); +}; /* * Per processor array of requests. diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index 8604cee..fb31a17 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -15,16 +15,17 @@ * along with this program. If not, see . */ +#include + #include -#include #include #include #include #include #include -void __noreturn tcb_context_load(struct tcb *tcb); -void __noreturn tcb_start(void); +noreturn void tcb_context_load(struct tcb *tcb); +noreturn void tcb_start(void); void tcb_context_restore(void); struct tcb *tcb_current_ptr __percpu; diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index b5b20bf..9c69a5e 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -23,8 +23,8 @@ #include #include +#include -#include #include #include @@ -70,7 +70,7 @@ tcb_set_current(struct tcb *tcb) * * Called with interrupts disabled. The caller context is lost. */ -void __noreturn tcb_load(struct tcb *tcb); +noreturn void tcb_load(struct tcb *tcb); /* * Context switch. diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 19ddaf7..1580fa5 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -36,7 +37,7 @@ #include struct trap_cpu_data { - unsigned char intr_stack[TRAP_STACK_SIZE] __aligned(CPU_DATA_ALIGN); + alignas(CPU_DATA_ALIGN) unsigned char intr_stack[TRAP_STACK_SIZE]; }; static struct trap_cpu_data trap_cpu_data __percpu; diff --git a/kern/intr.c b/kern/intr.c index 6590fca..769f4d9 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -22,6 +22,7 @@ * Shared interrupts are supported. */ +#include #include #include @@ -39,10 +40,10 @@ #include struct intr_handler { - struct list node; + alignas(CPU_L1_SIZE) struct list node; intr_handler_fn_t fn; void *arg; -} __aligned(CPU_L1_SIZE); +}; /* * Interrupt controller. @@ -67,11 +68,11 @@ struct intr_ctl { * span a cache line to avoid false sharing. */ struct intr_entry { - struct spinlock lock; + alignas(CPU_L1_SIZE) struct spinlock lock; struct intr_ctl *ctl; unsigned int cpu; struct list handlers; -} __aligned(CPU_L1_SIZE); +}; /* * Interrupt table. diff --git a/kern/kernel.h b/kern/kernel.h index 868066e..22cae43 100644 --- a/kern/kernel.h +++ b/kern/kernel.h @@ -18,7 +18,7 @@ #ifndef _KERN_KERNEL_H #define _KERN_KERNEL_H -#include +#include /* * Kernel properties. @@ -31,13 +31,13 @@ * * Interrupts must be disabled when calling this function. */ -void __noreturn kernel_main(void); +noreturn void kernel_main(void); /* * Entry point for APs. * * Interrupts must be disabled when calling this function. */ -void __noreturn kernel_ap_main(void); +noreturn void kernel_ap_main(void); #endif /* _KERN_KERNEL_H */ diff --git a/kern/kmem_i.h b/kern/kmem_i.h index d4671e2..beae6c4 100644 --- a/kern/kmem_i.h +++ b/kern/kmem_i.h @@ -18,6 +18,7 @@ #ifndef _KERN_KMEM_I_H #define _KERN_KMEM_I_H +#include #include #include @@ -30,13 +31,13 @@ * The flags member is a read-only CPU-local copy of the parent cache flags. */ struct kmem_cpu_pool { - struct mutex lock; + alignas(CPU_L1_SIZE) struct mutex lock; int flags; int size; int transfer_size; int nr_objs; void **array; -} __aligned(CPU_L1_SIZE); +}; /* * When a cache is created, its CPU pool type is determined from the buffer diff --git a/kern/llsync_i.h b/kern/llsync_i.h index dd5a617..e043eb9 100644 --- a/kern/llsync_i.h +++ b/kern/llsync_i.h @@ -19,6 +19,7 @@ #define _KERN_LLSYNC_I_H #include +#include #include #include @@ -57,7 +58,7 @@ struct llsync_data { * - apply optimistic accesses to reduce contention */ struct { - volatile unsigned int value __aligned(CPU_L1_SIZE); + alignas(CPU_L1_SIZE) volatile unsigned int value; } gcid; }; diff --git a/kern/panic.h b/kern/panic.h index cd0651a..e098057 100644 --- a/kern/panic.h +++ b/kern/panic.h @@ -18,12 +18,12 @@ #ifndef _KERN_PANIC_H #define _KERN_PANIC_H -#include +#include /* * Print the given message and halt the system immediately. */ -void __noreturn panic(const char *format, ...) +noreturn void panic(const char *format, ...) __attribute__((format(printf, 1, 2))); #endif /* _KERN_PANIC_H */ diff --git a/kern/sleepq.c b/kern/sleepq.c index f7c3245..d8bbbdd 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -33,9 +34,9 @@ #include struct sleepq_bucket { - struct spinlock lock; + alignas(CPU_L1_SIZE) struct spinlock lock; struct list list; -} __aligned(CPU_L1_SIZE); +}; struct sleepq { struct sleepq_bucket *bucket; diff --git a/kern/spinlock.c b/kern/spinlock.c index 68e8421..6b23b36 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -64,6 +64,7 @@ */ #include +#include #include #include @@ -128,7 +129,7 @@ struct spinlock_qnode { #endif struct spinlock_cpu_data { - struct spinlock_qnode qnodes[SPINLOCK_NR_CTXS - 1] __aligned(CPU_L1_SIZE); + alignas(CPU_L1_SIZE) struct spinlock_qnode qnodes[SPINLOCK_NR_CTXS - 1]; }; static struct spinlock_cpu_data spinlock_cpu_data __percpu; diff --git a/kern/thread.c b/kern/thread.c index eb0f1eb..a48a08a 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -82,10 +82,12 @@ */ #include +#include #include #include #include #include +#include #include #include @@ -227,7 +229,7 @@ struct thread_fs_runq { * return path) may violate the locking order. */ struct thread_runq { - struct spinlock lock; + alignas(CPU_L1_SIZE) struct spinlock lock; unsigned int cpu; unsigned int nr_threads; struct thread *current; @@ -258,7 +260,7 @@ struct thread_runq { struct syscnt sc_schedule_intrs; struct syscnt sc_tick_intrs; struct syscnt sc_boosts; -} __aligned(CPU_L1_SIZE); +}; /* * Operations of a scheduling class. @@ -322,7 +324,7 @@ static struct cpumap thread_idle_runqs; * There can be moderate bouncing on this word so give it its own cache line. */ static struct { - volatile unsigned long value __aligned(CPU_L1_SIZE); + alignas(CPU_L1_SIZE) volatile unsigned long value; } thread_fs_highest_round_struct; #define thread_fs_highest_round (thread_fs_highest_round_struct.value) @@ -1515,7 +1517,7 @@ thread_sched_idle_select_runq(struct thread *thread) panic("thread: idler threads cannot be awaken"); } -static void __noreturn +static noreturn void thread_sched_idle_panic(void) { panic("thread: only idle threads are allowed in the idle class"); diff --git a/kern/thread.h b/kern/thread.h index 24e3c67..760bb5e 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -36,11 +36,11 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -203,7 +203,7 @@ int thread_create(struct thread **threadp, const struct thread_attr *attr, /* * Terminate the calling thread. */ -void __noreturn thread_exit(void); +noreturn void thread_exit(void); /* * Wait for the given thread to terminate and release its resources. @@ -244,7 +244,7 @@ void thread_wakeup(struct thread *thread); * * Interrupts must be disabled when calling this function. */ -void __noreturn thread_run_scheduler(void); +noreturn void thread_run_scheduler(void); /* * Make the calling thread release the processor. diff --git a/kern/thread_i.h b/kern/thread_i.h index b7d3de1..3f35088 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -18,13 +18,13 @@ #ifndef _KERN_THREAD_I_H #define _KERN_THREAD_I_H +#include #include #include #include #include #include -#include #include #include #include @@ -97,7 +97,7 @@ struct thread_fs_data { * ( ) read-only */ struct thread { - struct tcb tcb; /* (r) */ + alignas(CPU_L1_SIZE) struct tcb tcb; /* (r) */ unsigned long nr_refs; /* (a) */ unsigned long flags; /* (a) */ @@ -178,7 +178,7 @@ struct thread { struct list task_node; /* (T) */ void *stack; /* (-) */ char name[THREAD_NAME_SIZE]; /* ( ) */ -} __aligned(CPU_L1_SIZE); +}; #define THREAD_ATTR_DETACHED 0x1 diff --git a/kern/turnstile.c b/kern/turnstile.c index 97416ae..24da2cd 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -44,6 +44,7 @@ */ #include +#include #include #include #include @@ -64,9 +65,9 @@ * (b) bucket */ struct turnstile_bucket { - struct spinlock lock; + alignas(CPU_L1_SIZE) struct spinlock lock; struct list list; /* (b) */ -} __aligned(CPU_L1_SIZE); +}; /* * Adding/removing waiters to/from a turnstile are performed while diff --git a/kern/work.c b/kern/work.c index 9925c19..5021d6f 100644 --- a/kern/work.c +++ b/kern/work.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -84,7 +85,7 @@ struct work_thread { * only use one queue. */ struct work_pool { - struct spinlock lock; + alignas(CPU_L1_SIZE) struct spinlock lock; int flags; struct work_queue queue0; struct work_queue queue1; @@ -97,7 +98,7 @@ struct work_pool { struct list available_threads; struct list dead_threads; BITMAP_DECLARE(bitmap, WORK_MAX_THREADS); -} __aligned(CPU_L1_SIZE); +}; static int work_thread_create(struct work_pool *pool, unsigned int id); static void work_thread_destroy(struct work_thread *worker); diff --git a/kern/xcall.c b/kern/xcall.c index 1c052ac..aab45b3 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -27,9 +28,9 @@ #include struct xcall { - xcall_fn_t fn; + alignas(CPU_L1_SIZE) xcall_fn_t fn; void *arg; -} __aligned(CPU_L1_SIZE); +}; /* * Per-CPU data. @@ -48,11 +49,11 @@ struct xcall { * between multiple cross-calls. */ struct xcall_cpu_data { - struct xcall send_calls[X15_MAX_CPUS]; + alignas(CPU_L1_SIZE) struct xcall send_calls[X15_MAX_CPUS]; struct xcall *recv_call; struct spinlock lock; -} __aligned(CPU_L1_SIZE); +}; static struct xcall_cpu_data xcall_cpu_data __percpu; diff --git a/vm/vm_page.c b/vm/vm_page.c index caa8c98..0067449 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -30,6 +30,7 @@ */ #include +#include #include #include #include @@ -75,12 +76,12 @@ * Per-processor cache of pages. */ struct vm_page_cpu_pool { - struct mutex lock; + alignas(CPU_L1_SIZE) struct mutex lock; int size; int transfer_size; int nr_pages; struct list pages; -} __aligned(CPU_L1_SIZE); +}; /* * Special order value for pages that aren't in a free list. Such pages are -- cgit v1.2.3 From e1f63c4796c21ac5799d153503259474e8ba66db Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 20:46:23 +0200 Subject: kern/macros: update Add comments to power-of-two alignment macros, remove functionality made standard in C11, remove the __alias macro, add the __noinline macro. --- kern/macros.h | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kern/macros.h b/kern/macros.h index 743bdc0..6259dce 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -24,9 +24,9 @@ #ifndef _KERN_MACROS_H #define _KERN_MACROS_H -#ifndef __ASSEMBLER__ -#include -#endif /* __ASSEMBLER__ */ +#if !defined(__GNUC__) || (__GNUC__ < 4) +#error "GCC 4+ required" +#endif /* * Attributes for variables that are mostly read and seldom changed. @@ -56,29 +56,36 @@ #define P2ALIGNED(x, a) (((x) & ((a) - 1)) == 0) #define ISP2(x) P2ALIGNED(x, x) -#define P2ALIGN(x, a) ((x) & -(a)) -#define P2ROUND(x, a) (-(-(x) & -(a))) -#define P2END(x, a) (-(~(x) & -(a))) +#define P2ALIGN(x, a) ((x) & -(a)) /* decreases if not aligned */ +#define P2ROUND(x, a) (-(-(x) & -(a))) /* increases if not aligned */ +#define P2END(x, a) (-(~(x) & -(a))) /* always increases */ #define structof(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) -#define alignof(x) __alignof__(x) - #define likely(expr) __builtin_expect(!!(expr), 1) #define unlikely(expr) __builtin_expect(!!(expr), 0) #define barrier() asm volatile("" : : : "memory") -#define __noreturn __attribute__((noreturn)) -#define __aligned(x) __attribute__((aligned(x))) +/* + * The following macros may be provided by the C environment. + */ + +#ifndef __noinline +#define __noinline __attribute__((noinline)) +#endif #ifndef __always_inline #define __always_inline inline __attribute__((always_inline)) -#endif /* __attribute__ */ +#endif +#ifndef __section #define __section(x) __attribute__((section(x))) +#endif + +#ifndef __packed #define __packed __attribute__((packed)) -#define __alias(x) __attribute__((alias(x))) +#endif #endif /* _KERN_MACROS_H */ -- cgit v1.2.3 From 0ca0de957fe46a4be177d5aabeb44deea259f741 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 25 Jun 2017 20:59:57 +0200 Subject: kern/xcall: fix minor style issue --- kern/xcall.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kern/xcall.c b/kern/xcall.c index aab45b3..fffe2bc 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -92,7 +92,8 @@ xcall_cpu_data_get_recv_call(const struct xcall_cpu_data *cpu_data) } static void -xcall_cpu_data_set_recv_call(struct xcall_cpu_data *cpu_data, struct xcall *call) +xcall_cpu_data_set_recv_call(struct xcall_cpu_data *cpu_data, + struct xcall *call) { atomic_store(&cpu_data->recv_call, call, ATOMIC_RELEASE); } -- cgit v1.2.3 From 4dfe9abddbe18036ff4e4e2422491cb928563d2e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 28 Jun 2017 01:02:08 +0200 Subject: kern/string: use the restrict keyword where appropriate --- kern/string.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kern/string.h b/kern/string.h index 35490ff..7cd79a6 100644 --- a/kern/string.h +++ b/kern/string.h @@ -20,13 +20,13 @@ #include -void * memcpy(void *dest, const void *src, size_t n); +void * memcpy(void * restrict dest, const void * restrict src, size_t n); void * memmove(void *dest, const void *src, size_t n); void * memset(void *s, int c, size_t n); int memcmp(const void *s1, const void *s2, size_t n); size_t strlen(const char *s); -char * strcpy(char *dest, const char *src); -size_t strlcpy(char *dest, const char *src, size_t n); +char * strcpy(char * restrict dest, const char *restrict src); +size_t strlcpy(char * restrict dest, const char * restrict src, size_t n); int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); char * strchr(const char *s, int c); -- cgit v1.2.3 From 2d2e8f93303fc7b19ab19020013145144be90fa3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 28 Jun 2017 01:02:34 +0200 Subject: kern/string: make strncmp handling of size 0 more intuitive --- kern/string.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kern/string.c b/kern/string.c index 7508e79..6c40478 100644 --- a/kern/string.c +++ b/kern/string.c @@ -21,6 +21,7 @@ #include #include +#include #include #ifndef STRING_ARCH_MEMCPY @@ -189,8 +190,9 @@ strncmp(const char *s1, const char *s2, size_t n) { char c1, c2; - c1 = '\0'; - c2 = '\0'; + if (unlikely(n == 0)) { + return 0; + } while ((n != 0) && (c1 = *s1) == (c2 = *s2)) { if (c1 == '\0') { -- cgit v1.2.3 From 982e80f759f3174a9b91bbfa08221c2a11146417 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 28 Jun 2017 01:04:49 +0200 Subject: x86/string: make strlen return value computation more intuitive --- arch/x86/machine/string.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/string.c b/arch/x86/machine/string.c index 1f1cab4..aa8795c 100644 --- a/arch/x86/machine/string.c +++ b/arch/x86/machine/string.c @@ -103,7 +103,7 @@ strlen(const char *s) : "+D" (s), "+c" (n) : "a" (0) : "memory"); - return ~n - 1; + return (size_t)-2 - n; } #endif /* STRING_ARCH_STRLEN */ -- cgit v1.2.3 From 66f04e038e510e27037c0c8a6673c5940c8b22f1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 28 Jun 2017 01:05:46 +0200 Subject: x86/string: implement assembly versions of strncmp and strchr --- arch/x86/machine/string.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/machine/string.h | 2 ++ 2 files changed, 50 insertions(+) diff --git a/arch/x86/machine/string.c b/arch/x86/machine/string.c index aa8795c..e4f7f0c 100644 --- a/arch/x86/machine/string.c +++ b/arch/x86/machine/string.c @@ -18,6 +18,7 @@ #include #include +#include #include #ifdef STRING_ARCH_MEMCPY @@ -145,3 +146,50 @@ strcmp(const char *s1, const char *s2) return (int)c1 - (int)c2; } #endif /* STRING_ARCH_STRCMP */ + +#ifdef STRING_ARCH_STRNCMP +int +strncmp(const char *s1, const char *s2, size_t n) +{ + unsigned char c1, c2; + + if (unlikely(n == 0)) { + return 0; + } + + asm volatile("1:\n" + "lodsb\n" + "scasb\n" + "jne 1f\n" + "testb %%al, %%al\n" + "jz 1f\n" + "dec %2\n" + "jnz 1b\n" + "1:\n" + : "+D" (s1), "+S" (s2), "+c" (n) + : : "al", "memory"); + c1 = *(((const unsigned char *)s1) - 1); + c2 = *(((const unsigned char *)s2) - 1); + return (int)c1 - (int)c2; +} +#endif /* STRING_ARCH_STRNCMP */ + +#ifdef STRING_ARCH_STRCHR +char * +strchr(const char *s, int c) +{ + asm volatile("1:\n" + "lodsb\n" + "cmpb %%al, %1\n" + "je 1f\n" + "testb %%al, %%al\n" + "jnz 1b\n" + "mov $1, %0\n" + "1:\n" + "dec %0\n" + : "+S" (s) + : "c" ((char)c) + : "al", "memory"); + return (char *)s; +} +#endif /* STRING_ARCH_STRCHR */ diff --git a/arch/x86/machine/string.h b/arch/x86/machine/string.h index d57bb24..6111ba1 100644 --- a/arch/x86/machine/string.h +++ b/arch/x86/machine/string.h @@ -28,5 +28,7 @@ #define STRING_ARCH_STRLEN #define STRING_ARCH_STRCPY #define STRING_ARCH_STRCMP +#define STRING_ARCH_STRNCMP +#define STRING_ARCH_STRCHR #endif /* _X86_STRING_H */ -- cgit v1.2.3 From 3a00ba63aa6d52092ff0674e97147e70da9c45c2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 28 Jun 2017 01:06:27 +0200 Subject: x86/string: explain the memory clobber in read-only functions --- arch/x86/machine/string.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/machine/string.c b/arch/x86/machine/string.c index e4f7f0c..c44b62d 100644 --- a/arch/x86/machine/string.c +++ b/arch/x86/machine/string.c @@ -13,6 +13,11 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * + * Despite comparison and scan instructions not having side-effects, the + * memory clobber is used because the compiler cannot infer dependencies + * on the memory referenced by the pointers. */ #include -- cgit v1.2.3 From 9b81ecb970b4929be2a210ed8539e297a73003f1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 18:52:56 +0200 Subject: Minor style fixes --- arch/x86/machine/acpi.c | 12 ++++++++---- kern/fmt.c | 3 ++- kern/string.c | 3 ++- test/test_llsync_defer.c | 3 ++- vm/vm_kmem.c | 3 ++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 4da4d90..36604f2 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -174,10 +174,11 @@ acpi_table_required(const struct acpi_sdth *table) acpi_table_sig(table, sig); - for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) { if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { return 1; } + } return 0; } @@ -190,7 +191,7 @@ acpi_register_table(struct acpi_sdth *table) acpi_table_sig(table, sig); - for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) { if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { if (acpi_table_addrs[i].table != NULL) { log_warning("acpi: table %s ignored: already registered", sig); @@ -200,6 +201,7 @@ acpi_register_table(struct acpi_sdth *table) acpi_table_addrs[i].table = table; return; } + } log_warning("acpi: table '%s' ignored: unknown table", sig); } @@ -209,10 +211,11 @@ acpi_lookup_table(const char *sig) { size_t i; - for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) { if (strcmp(sig, acpi_table_addrs[i].sig) == 0) { return acpi_table_addrs[i].table; } + } return NULL; } @@ -222,11 +225,12 @@ acpi_check_tables(void) { size_t i; - for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) + for (i = 0; i < ARRAY_SIZE(acpi_table_addrs); i++) { if (acpi_table_addrs[i].table == NULL) { log_err("acpi: table %s missing", acpi_table_addrs[i].sig); return -1; } + } return 0; } diff --git a/kern/fmt.c b/kern/fmt.c index 57ea069..d1067e5 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -676,10 +676,11 @@ fmt_sprintf_state_produce_str(struct fmt_sprintf_state *state) len = 0; - for (len = 0; s[len] != '\0'; len++) + for (len = 0; s[len] != '\0'; len++) { if (len == state->precision) { break; } + } if (!(state->flags & FMT_FORMAT_LEFT_JUSTIFY)) { while (len < state->width) { diff --git a/kern/string.c b/kern/string.c index 6c40478..6c9c8ab 100644 --- a/kern/string.c +++ b/kern/string.c @@ -104,10 +104,11 @@ memcmp(const void *s1, const void *s2, size_t n) a1 = s1; a2 = s2; - for (i = 0; i < n; i++) + for (i = 0; i < n; i++) { if (a1[i] != a2[i]) { return (int)a1[i] - (int)a2[i]; } + } return 0; } diff --git a/test/test_llsync_defer.c b/test/test_llsync_defer.c index 249c53a..4b8afbc 100644 --- a/test/test_llsync_defer.c +++ b/test/test_llsync_defer.c @@ -167,10 +167,11 @@ test_read(void *arg) s = (const unsigned char *)pdsc->addr; if (s != NULL) { - for (j = 0; j < PAGE_SIZE; j++) + for (j = 0; j < PAGE_SIZE; j++) { if (s[j] != TEST_VALIDATION_BYTE) { panic("invalid content"); } + } if ((i % TEST_LOOPS_PER_PRINT) == 0) { printf("read "); diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 488ae30..466019a 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -181,9 +181,10 @@ vm_kmem_map_pa(phys_addr_t pa, size_t size, return NULL; } - for (offset = 0; offset < map_size; offset += PAGE_SIZE) + for (offset = 0; offset < map_size; offset += PAGE_SIZE) { pmap_enter(kernel_pmap, map_va + offset, start + offset, VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); + } pmap_update(kernel_pmap); -- cgit v1.2.3 From d6a18f61e0a53d9bd68cc927831423d7454410af Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 18:32:18 +0200 Subject: kern/shutdown: new module --- Makefrag.am | 2 + arch/x86/machine/boot.c | 2 + kern/kernel.c | 2 + kern/shutdown.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ kern/shutdown.h | 39 +++++++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 kern/shutdown.c create mode 100644 kern/shutdown.h diff --git a/Makefrag.am b/Makefrag.am index 886ec1f..8a966b2 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -74,6 +74,8 @@ x15_SOURCES += \ kern/semaphore.c \ kern/semaphore.h \ kern/semaphore_i.h \ + kern/shutdown.c \ + kern/shutdown.h \ kern/sleepq.c \ kern/sleepq.h \ kern/spinlock.c \ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 7ce7b85..571b04f 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -514,6 +515,7 @@ boot_main(void) vm_setup(); boot_save_data(); biosmem_free_usable(); + shutdown_setup(); intr_setup(); cpu_mp_probe(); atcons_setup(); diff --git a/kern/kernel.c b/kern/kernel.c index 9aa5c35..6c562f4 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ kernel_main(void) llsync_setup(); sref_setup(); vm_page_log_info(); + shutdown_register_shell_cmds(); log_start(); #ifdef X15_RUN_TEST_MODULE diff --git a/kern/shutdown.c b/kern/shutdown.c new file mode 100644 index 0000000..11a5183 --- /dev/null +++ b/kern/shutdown.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 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 3 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 + +static struct plist shutdown_ops_list; + +#ifdef X15_SHELL + +static void +shutdown_shell_halt(int argc, char **argv) +{ + (void)argc; + (void)argv; + + shutdown_halt(); +} + +static void +shutdown_shell_reboot(int argc, char **argv) +{ + (void)argc; + (void)argv; + + shutdown_reboot(); +} + +static struct shell_cmd shutdown_shell_cmds[] = { + SHELL_CMD_INITIALIZER("shutdown_halt", shutdown_shell_halt, + "shutdown_halt", + "halt the system"), + SHELL_CMD_INITIALIZER("shutdown_reboot", shutdown_shell_reboot, + "shutdown_reboot", + "reboot the system"), +}; + +#endif /* X15_SHELL */ + +void __init +shutdown_setup(void) +{ + plist_init(&shutdown_ops_list); +} + +void __init +shutdown_register_shell_cmds(void) +{ + SHELL_REGISTER_CMDS(shutdown_shell_cmds); +} + +void __init +shutdown_register(struct shutdown_ops *ops, unsigned int priority) +{ + plist_node_init(&ops->node, priority); + plist_add(&shutdown_ops_list, &ops->node); +} + +static void +shutdown_halt_other_cpus(void) +{ + cpu_intr_disable(); + cpu_halt_broadcast(); +} + +void +shutdown_halt(void) +{ + shutdown_halt_other_cpus(); + printf("shutdown: system halted\n"); + cpu_halt(); +} + +void +shutdown_reboot(void) +{ + struct shutdown_ops *ops; + + if (plist_empty(&shutdown_ops_list)) { + printf("shutdown: no reset operation available, halting\n"); + shutdown_halt(); + } + + shutdown_halt_other_cpus(); + printf("shutdown: rebooting...\n"); + + plist_for_each_entry_reverse(&shutdown_ops_list, ops, node) { + ops->reset(); + } + + cpu_halt(); +} diff --git a/kern/shutdown.h b/kern/shutdown.h new file mode 100644 index 0000000..f7bc187 --- /dev/null +++ b/kern/shutdown.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_SHUTDOWN_H +#define _KERN_SHUTDOWN_H + +#include + +#include + +struct shutdown_ops { + struct plist_node node; + void (*reset)(void); +}; + +void shutdown_setup(void); + +void shutdown_register_shell_cmds(void); + +void shutdown_register(struct shutdown_ops *ops, unsigned int priority); + +noreturn void shutdown_halt(void); +noreturn void shutdown_reboot(void); + +#endif /* _KERN_SHUTDOWN_H */ -- cgit v1.2.3 From b713ea5c3125dd68320bd01aabc40212a73777d2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 18:57:55 +0200 Subject: x86/trap: rename double fault trigger function --- arch/x86/machine/trap.h | 2 +- test/test_x86_double_fault.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index 646eda3..f8d0a16 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -137,7 +137,7 @@ struct trap_frame { typedef void (*trap_handler_fn_t)(struct trap_frame *); static inline void -trap_test_double_fault(void) +trap_trigger_double_fault(void) { printf("trap: double fault test\n"); asm volatile("movl $0xdead, %esp; push $0"); diff --git a/test/test_x86_double_fault.c b/test/test_x86_double_fault.c index cd12758..20596fe 100644 --- a/test/test_x86_double_fault.c +++ b/test/test_x86_double_fault.c @@ -24,5 +24,5 @@ void test_setup(void) { - trap_test_double_fault(); + trap_trigger_double_fault(); } -- cgit v1.2.3 From cbe55a5d6e39eb4e208615b291f367ac8054f6b1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 18:58:33 +0200 Subject: x86/cpu: register triple fault reset method --- arch/x86/machine/cpu.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index a53d8e2..debacf0 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,13 @@ #define CPU_MP_CMOS_DATA_RESET_WARM 0x0a #define CPU_MP_CMOS_RESET_VECTOR 0x467 +/* + * Priority of the shutdown operations. + * + * Last resort, lower than everything else. + */ +#define CPU_SHUTDOWN_PRIORITY 0 + /* * Gate descriptor. */ @@ -103,7 +111,7 @@ struct cpu_sysseg_desc { struct cpu_pseudo_desc { uint16_t limit; - unsigned long address; + uintptr_t address; } __packed; void *cpu_local_area __percpu; @@ -413,12 +421,12 @@ cpu_idt_set_double_fault(void (*isr)(void)) } static void -cpu_load_idt(void) +cpu_load_idt(const void *idt, size_t size) { struct cpu_pseudo_desc idtr; - idtr.address = (unsigned long)cpu_idt; - idtr.limit = sizeof(cpu_idt) - 1; + idtr.address = (uintptr_t)idt; + idtr.limit = size - 1; asm volatile("lidt %0" : : "m" (idtr)); } @@ -443,7 +451,7 @@ cpu_init(struct cpu *cpu) #ifndef __LP64__ cpu_init_double_fault_tss(cpu); #endif /* __LP64__ */ - cpu_load_idt(); + cpu_load_idt(cpu_idt, sizeof(cpu_idt)); eax = 0; cpu_cpuid(&eax, &ebx, &ecx, &edx); @@ -638,6 +646,17 @@ cpu_mp_register_lapic(unsigned int apic_id, int is_bsp) cpu_nr_active++; } +static void +cpu_shutdown_reset(void) +{ + cpu_load_idt(NULL, 1); + trap_trigger_double_fault(); +} + +static struct shutdown_ops cpu_shutdown_ops = { + .reset = cpu_shutdown_reset, +}; + void __init cpu_mp_probe(void) { @@ -656,6 +675,10 @@ cpu_mp_probe(void) } log_info("cpu: %u processor(s) configured", cpu_count()); + + if (cpu_count() == 1) { + shutdown_register(&cpu_shutdown_ops, CPU_SHUTDOWN_PRIORITY); + } } void __init -- cgit v1.2.3 From 6259bf966b1fd8b7e6952ba43d92596890a3bf21 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 19:06:30 +0200 Subject: x86/acpi: register system I/O reset method --- arch/x86/machine/acpi.c | 105 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 36604f2..93dc325 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,13 +38,24 @@ #include /* - * Alignment of the RSDP. + * Priority of the shutdown operations. + * + * Higher than all other methods. */ +#define ACPI_SHUTDOWN_PRIORITY 10 + +#define ACPI_GAS_ASID_SYSIO 1 + +struct acpi_gas { + uint8_t asid; + uint8_t reg_width; + uint8_t reg_offset; + uint8_t access_size; + uint64_t addr; +} __packed; + #define ACPI_RSDP_ALIGN 16 -/* - * Signature of the root system description pointer. - */ #define ACPI_RSDP_SIG "RSD PTR " struct acpi_rsdp { @@ -54,9 +66,6 @@ struct acpi_rsdp { uint32_t rsdt_address; } __packed; -/* - * Size of a buffer which can store a table signature as a string. - */ #define ACPI_SIG_SIZE 5 struct acpi_sdth { @@ -76,9 +85,6 @@ struct acpi_rsdt { uint32_t entries[0]; } __packed; -/* - * MADT entry type codes. - */ #define ACPI_MADT_ENTRY_LAPIC 0 #define ACPI_MADT_ENTRY_IOAPIC 1 #define ACPI_MADT_ENTRY_ISO 2 @@ -149,16 +155,33 @@ for (acpi_madt_iter_init(iter, madt); \ acpi_madt_iter_valid(iter); \ acpi_madt_iter_next(iter)) +#define ACPI_FADT_FL_RESET_REG_SUP 0x400 + +struct acpi_fadt { + union { + struct acpi_sdth header; + char unused[112]; + }; + + uint32_t flags; + struct acpi_gas reset_reg; + uint8_t reset_value; +} __packed; + struct acpi_table_addr { const char *sig; struct acpi_sdth *table; }; static struct acpi_table_addr acpi_table_addrs[] __initdata = { - { "RSDT", NULL }, - { "APIC", NULL } + { "RSDT", NULL }, + { "APIC", NULL }, + { "FACP", NULL }, }; +static struct acpi_gas acpi_reset_reg; +static uint8_t acpi_reset_value; + static void __init acpi_table_sig(const struct acpi_sdth *table, char sig[ACPI_SIG_SIZE]) { @@ -581,6 +604,63 @@ acpi_load_madt(void) } } +static void +acpi_shutdown_reset_sysio(uint64_t addr) +{ + if (addr > UINT16_MAX) { + log_warning("acpi: invalid sysio address"); + return; + } + + io_write_byte((uint16_t)addr, acpi_reset_value); +} + +static void +acpi_shutdown_reset(void) +{ + if ((acpi_reset_reg.reg_width != 8) || (acpi_reset_reg.reg_offset != 0)) { + log_warning("acpi: invalid reset register"); + return; + } + + switch (acpi_reset_reg.asid) { + case ACPI_GAS_ASID_SYSIO: + acpi_shutdown_reset_sysio(acpi_reset_reg.addr); + break; + default: + log_warning("acpi: unsupported reset register type"); + } +} + +static struct shutdown_ops acpi_shutdown_ops = { + .reset = acpi_shutdown_reset, +}; + +static void __init +acpi_load_fadt(void) +{ + const struct acpi_sdth *table; + const struct acpi_fadt *fadt; + + table = acpi_lookup_table("FACP"); + + if (table == NULL) { + log_debug("acpi: unable to find FADT table"); + return; + } + + fadt = structof(table, struct acpi_fadt, header); + + if (!(fadt->flags & ACPI_FADT_FL_RESET_REG_SUP)) { + log_debug("acpi: reset register not supported"); + return; + } + + acpi_reset_reg = fadt->reset_reg; + acpi_reset_value = fadt->reset_value; + shutdown_register(&acpi_shutdown_ops, ACPI_SHUTDOWN_PRIORITY); +} + int __init acpi_setup(void) { @@ -601,6 +681,7 @@ acpi_setup(void) acpi_info(); acpi_load_madt(); + acpi_load_fadt(); acpi_free_tables(); return 0; } -- cgit v1.2.3 From d27f4b6eab1c1a7764ae2861078490012b9e9399 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 1 Jul 2017 19:07:37 +0200 Subject: kern/shell: improve help output format --- kern/shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/shell.c b/kern/shell.c index 81fe013..6be17cd 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -668,7 +668,7 @@ shell_cmd_help(int argc, char *argv[]) shell_cmd_acquire(); for (cmd = shell_list; cmd != NULL; cmd = cmd->ls_next) { - printf("%13s %s\n", cmd->name, cmd->short_desc); + printf("%19s %s\n", cmd->name, cmd->short_desc); } shell_cmd_release(); -- cgit v1.2.3 From b041edd4557f7b96be6e6c6d7d13772b3d7040f1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 11:13:46 +0200 Subject: kern/rdxtree: use 64-bits keys --- kern/rdxtree.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/rdxtree.h b/kern/rdxtree.h index e3a2ba0..02ae048 100644 --- a/kern/rdxtree.h +++ b/kern/rdxtree.h @@ -29,7 +29,7 @@ #include #include -typedef uint32_t rdxtree_key_t; +typedef uint64_t rdxtree_key_t; /* * Radix tree initialization flags. -- cgit v1.2.3 From a3e0c923aa805b8bce6aa370960ecd8cb5ff3ecf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 11:23:22 +0200 Subject: kern/rdxtree: provide a lockless-aware slot load accessor --- kern/rdxtree.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kern/rdxtree.h b/kern/rdxtree.h index 02ae048..a30512c 100644 --- a/kern/rdxtree.h +++ b/kern/rdxtree.h @@ -29,6 +29,8 @@ #include #include +#include + typedef uint64_t rdxtree_key_t; /* @@ -154,6 +156,12 @@ rdxtree_lookup_slot(const struct rdxtree *tree, rdxtree_key_t key) return rdxtree_lookup_common(tree, key, 1); } +static inline void * +rdxtree_load_slot(void **slot) +{ + return llsync_read_ptr(*slot); +} + /* * Replace a pointer in a tree. * -- cgit v1.2.3 From f1e9c4ca51ed55fd2ab9cb36ad22793f8f7db8ec Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 11:28:11 +0200 Subject: kern/sref: add TODO to review sref_weakref_tryget --- kern/sref.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kern/sref.c b/kern/sref.c index 7c4f14e..7167eff 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -288,6 +288,7 @@ sref_weakref_tryget(struct sref_weakref *weakref) { uintptr_t addr, oldval, newval; + /* TODO Review */ do { addr = atomic_load(&weakref->addr, ATOMIC_RELAXED); newval = addr & SREF_WEAKREF_MASK; -- cgit v1.2.3 From a822e80fe2b15cb93d01388b2d2a9f944ca72372 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 12:02:14 +0200 Subject: kern/rdxtree: force internal nodes allocation through the page allocator Radix trees are going to be used to VM objects, which makes them a dependency for proper virtual memory operations. They must not use virtual memory themselves. --- kern/rdxtree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/rdxtree.c b/kern/rdxtree.c index 9d12439..77f6a68 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -900,5 +900,5 @@ rdxtree_setup(void) { kmem_cache_init(&rdxtree_node_cache, "rdxtree_node", sizeof(struct rdxtree_node), 0, - rdxtree_node_ctor, 0); + rdxtree_node_ctor, KMEM_CACHE_PAGE_ONLY); } -- cgit v1.2.3 From 62142bef38dee8cc8878290a8775da2ae8c1d86d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 12:07:46 +0200 Subject: vm/vm_object: new module Start a very simple VM object implementation for page tracking only. The locking protocol is still not well defined, especially for pages. The only purpose of the current code is to allow the kernel virtual memory interface to release pages on physical mapping creation errors. --- Makefrag.am | 3 ++ vm/vm_object.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ vm/vm_object.h | 78 ++++++++++++++++++++++++++++ vm/vm_object_types.h | 36 +++++++++++++ vm/vm_page.c | 23 +++++++++ vm/vm_page.h | 79 ++++++++++++++++++++++++++++ vm/vm_setup.c | 4 +- 7 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 vm/vm_object.c create mode 100644 vm/vm_object.h create mode 100644 vm/vm_object_types.h diff --git a/Makefrag.am b/Makefrag.am index 886ec1f..025c0bf 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -115,6 +115,9 @@ x15_SOURCES += \ vm/vm_kmem.h \ vm/vm_map.c \ vm/vm_map.h \ + vm/vm_object.c \ + vm/vm_object.h \ + vm/vm_object_types.h \ vm/vm_page.c \ vm/vm_page.h \ vm/vm_prot.h \ diff --git a/vm/vm_object.c b/vm/vm_object.c new file mode 100644 index 0000000..874b26f --- /dev/null +++ b/vm/vm_object.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * This implementation is based on the paper "A lockless pagecache in Linux" + * by Nick Piggin. It allows looking up pages without contention on VM objects. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +void __init +vm_object_setup(void) +{ +} + +void __init +vm_object_init(struct vm_object *object, uint64_t size) +{ + assert(vm_page_aligned(size)); + + mutex_init(&object->lock); + rdxtree_init(&object->pages, 0); + object->size = size; + object->nr_pages = 0; +} + +int +vm_object_insert(struct vm_object *object, struct vm_page *page, + uint64_t offset) +{ + int error; + + assert(vm_page_aligned(offset)); + + /* + * The page may have no references. Add one before publishing + * so that concurrent lookups succeed. + */ + vm_page_ref(page); + + mutex_lock(&object->lock); + + if (offset >= object->size) { + error = ERROR_INVAL; + goto error; + } + + error = rdxtree_insert(&object->pages, vm_page_atop(offset), page); + + if (error) { + goto error; + } + + vm_page_link(page, object, offset); + object->nr_pages++; + assert(object->nr_pages != 0); + + mutex_unlock(&object->lock); + + return 0; + +error: + mutex_unlock(&object->lock); + + vm_page_unref(page); + + return error; +} + +void +vm_object_remove(struct vm_object *object, uint64_t start, uint64_t end) +{ + struct vm_page *page; + uint64_t offset; + + assert(vm_page_aligned(start)); + assert(vm_page_aligned(end)); + assert(start <= end); + + mutex_lock(&object->lock); + + for (offset = start; offset < end; offset += PAGE_SIZE) { + page = rdxtree_remove(&object->pages, vm_page_atop(offset)); + + if (page == NULL) { + continue; + } + + vm_page_unlink(page); + vm_page_unref(page); + assert(object->nr_pages != 0); + object->nr_pages--; + } + + mutex_unlock(&object->lock); +} + +struct vm_page * +vm_object_lookup(struct vm_object *object, uint64_t offset) +{ + struct vm_page *page; + int error; + + llsync_read_enter(); + + do { + page = rdxtree_lookup(&object->pages, vm_page_atop(offset)); + + if (page == NULL) { + break; + } + + error = vm_page_tryref(page); + } while (error); + + llsync_read_exit(); + + return page; +} diff --git a/vm/vm_object.h b/vm/vm_object.h new file mode 100644 index 0000000..046937f --- /dev/null +++ b/vm/vm_object.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Virtual memory object. + * + * The purpose of VM objects is to track pages that are resident in + * physical memory. They collectively form the page cache. + */ + +#ifndef _VM_OBJECT_H +#define _VM_OBJECT_H + +#include + +#include +#include +#include + +struct vm_object; + +/* + * Initialize the vm_object module. + */ +void vm_object_setup(void); + +/* + * Initialize a VM object. + */ +void vm_object_init(struct vm_object *object, uint64_t size); + +/* + * Insert a page into a VM object. + * + * The offset must be page-aligned. + * + * The page becomes managed, and gains a reference. If successful, + * the reference is kept. Otherwise it's dropped. If the page had + * no references on entry, and a failure occurs, the page is freed. + */ +int vm_object_insert(struct vm_object *object, struct vm_page *page, + uint64_t offset); + +/* + * Remove pages from a VM object. + * + * The range boundaries must be page-aligned. + * + * Holes in the given range are silently skipped. Pages that are removed + * become unmanaged and lose a reference. + */ +void vm_object_remove(struct vm_object *object, uint64_t start, uint64_t end); + +/* + * Look up a page in a VM object. + * + * The offset must be page-aligned. + * + * If successful, the returned page gains a reference. Note that, if a valid + * page is returned, it may already have been removed from the object, or + * moved at a different offset. + */ +struct vm_page * vm_object_lookup(struct vm_object *object, uint64_t offset); + +#endif /* _VM_OBJECT_H */ diff --git a/vm/vm_object_types.h b/vm/vm_object_types.h new file mode 100644 index 0000000..4026002 --- /dev/null +++ b/vm/vm_object_types.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _VM_OBJECT_TYPES_H +#define _VM_OBJECT_TYPES_H + +#include + +#include +#include + +struct vm_object { + struct mutex lock; + struct rdxtree pages; + uint64_t size; + unsigned long nr_pages; +}; + +#endif /* _VM_OBJECT_TYPES_H */ diff --git a/vm/vm_page.c b/vm/vm_page.c index 0067449..950d04a 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -167,6 +167,10 @@ vm_page_init(struct vm_page *page, unsigned short zone_index, phys_addr_t pa) page->zone_index = zone_index; page->order = VM_PAGE_ORDER_UNLISTED; page->phys_addr = pa; + + page->nr_refs = 0; + + page->object = NULL; } void @@ -740,6 +744,7 @@ vm_page_setup(void) SHELL_REGISTER_CMDS(vm_page_shell_cmds); } +/* TODO Rename to avoid confusion with "managed pages" */ void __init vm_page_manage(struct vm_page *page) { @@ -767,6 +772,22 @@ vm_page_lookup(phys_addr_t pa) return NULL; } +static bool +vm_page_block_referenced(const struct vm_page *page, unsigned int order) +{ + unsigned int i, nr_pages; + + nr_pages = 1 << order; + + for (i = 0; i < nr_pages; i++) { + if (vm_page_referenced(&page[i])) { + return true; + } + } + + return false; +} + struct vm_page * vm_page_alloc(unsigned int order, unsigned int selector, unsigned short type) { @@ -777,6 +798,7 @@ vm_page_alloc(unsigned int order, unsigned int selector, unsigned short type) page = vm_page_zone_alloc(&vm_page_zones[i], order, type); if (page != NULL) { + assert(!vm_page_block_referenced(page, order)); return page; } } @@ -788,6 +810,7 @@ void vm_page_free(struct vm_page *page, unsigned int order) { assert(page->zone_index < ARRAY_SIZE(vm_page_zones)); + assert(!vm_page_block_referenced(page, order)); vm_page_zone_free(&vm_page_zones[page->zone_index], page, order); } diff --git a/vm/vm_page.h b/vm/vm_page.h index 026c118..ea267db 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -16,15 +16,20 @@ * * * Physical page management. + * + * A page is said to be managed if it's linked to a VM object, in which + * case there is at least one reference to it. */ #ifndef _VM_VM_PAGE_H #define _VM_VM_PAGE_H #include +#include #include #include +#include #include #include #include @@ -32,11 +37,14 @@ #include #include #include +#include /* * Address/page conversion and rounding macros (not inline functions to * be easily usable on both virtual and physical addresses, which may not * have the same type size). + * + * TODO Rename btop and ptob. */ #define vm_page_atop(addr) ((addr) >> PAGE_SHIFT) #define vm_page_ptoa(page) ((page) << PAGE_SHIFT) @@ -80,6 +88,12 @@ struct vm_page { unsigned short order; phys_addr_t phys_addr; void *priv; + + unsigned int nr_refs; + + /* VM object back reference */ + struct vm_object *object; + uint64_t offset; }; static inline unsigned short @@ -139,6 +153,21 @@ vm_page_get_priv(const struct vm_page *page) return page->priv; } +static inline void +vm_page_link(struct vm_page *page, struct vm_object *object, uint64_t offset) +{ + assert(object != NULL); + page->object = object; + page->offset = offset; +} + +static inline void +vm_page_unlink(struct vm_page *page) +{ + assert(page->object != NULL); + page->object = NULL; +} + /* * Load physical memory into the vm_page module at boot time. * @@ -193,12 +222,16 @@ struct vm_page * vm_page_lookup(phys_addr_t pa); * * The selector is used to determine the zones from which allocation can * be attempted. + * + * If successful, the returned pages have no references. */ struct vm_page * vm_page_alloc(unsigned int order, unsigned int selector, unsigned short type); /* * Release a block of 2^order physical pages. + * + * The pages must have no references. */ void vm_page_free(struct vm_page *page, unsigned int order); @@ -212,4 +245,50 @@ const char * vm_page_zone_name(unsigned int zone_index); */ void vm_page_log_info(void); +static inline bool +vm_page_referenced(const struct vm_page *page) +{ + return atomic_load(&page->nr_refs, ATOMIC_RELAXED) != 0; +} + +static inline void +vm_page_ref(struct vm_page *page) +{ + unsigned int nr_refs; + + nr_refs = atomic_fetch_add(&page->nr_refs, 1, ATOMIC_RELAXED); + assert(nr_refs != (unsigned int)-1); +} + +static inline void +vm_page_unref(struct vm_page *page) +{ + unsigned int nr_refs; + + nr_refs = atomic_fetch_sub_acq_rel(&page->nr_refs, 1); + assert(nr_refs != 0); + + if (nr_refs == 1) { + vm_page_free(page, 0); + } +} + +static inline int +vm_page_tryref(struct vm_page *page) +{ + unsigned int nr_refs, prev; + + do { + nr_refs = atomic_load(&page->nr_refs, ATOMIC_RELAXED); + + if (nr_refs == 0) { + return ERROR_AGAIN; + } + + prev = atomic_cas_acquire(&page->nr_refs, nr_refs, nr_refs + 1); + } while (prev != nr_refs); + + return 0; +} + #endif /* _VM_VM_PAGE_H */ diff --git a/vm/vm_setup.c b/vm/vm_setup.c index 0957815..77f5fdb 100644 --- a/vm/vm_setup.c +++ b/vm/vm_setup.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -29,8 +30,9 @@ vm_setup(void) { vm_page_setup(); kmem_setup(); - vm_map_setup(); rdxtree_setup(); + vm_object_setup(); + vm_map_setup(); pmap_setup(); percpu_setup(); } -- cgit v1.2.3 From d9ed692fc0c7563c0f9574f3e2bc57f64b818005 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 14:38:25 +0200 Subject: kern/llsync: set ready on first registration Processor registration assumes no deferred work could be queued before, which may not be true at boot time, after initializing the llsync module but before starting the scheduler. --- kern/llsync.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kern/llsync.c b/kern/llsync.c index e339869..8ba87b6 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -102,8 +102,6 @@ llsync_setup(void) cpu_data = percpu_ptr(llsync_cpu_data, i); work_queue_init(&cpu_data->queue0); } - - llsync_is_ready = true; } static void @@ -181,6 +179,10 @@ llsync_register(void) unsigned long flags; unsigned int cpu; + if (!llsync_is_ready) { + llsync_is_ready = true; + } + cpu = cpu_id(); cpu_data = llsync_get_cpu_data(); -- cgit v1.2.3 From 2a98de687a0915701dc5032e9717edf363e531b9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 14:42:19 +0200 Subject: vm/vm_kmem: create a VM object to track mapped kernel pages --- arch/x86/machine/pmap.h | 1 + vm/vm_kmem.c | 92 +++++++++++++++++++++++++++++++++---------------- vm/vm_kmem.h | 5 +++ vm/vm_setup.c | 2 ++ 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 2b23126..8404258 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -16,6 +16,7 @@ * * * TODO Comment. + * TODO Rename MIN/MAX to START/END. */ #ifndef _X86_PMAP_H diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 488ae30..da372b0 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -13,9 +13,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * - * TODO Rework so that pmap update errors can be handled. */ #include @@ -33,6 +30,7 @@ #include #include #include +#include #include #include @@ -42,6 +40,24 @@ static struct vm_map kernel_map_store; struct vm_map *kernel_map __read_mostly = &kernel_map_store; +static struct vm_object vm_kmem_kernel_object; + +static uint64_t +vm_kmem_offset(uintptr_t va) +{ + assert(va >= PMAP_MIN_KMEM_ADDRESS); + return va - PMAP_MIN_KMEM_ADDRESS; +} + +void __init +vm_kmem_setup(void) +{ + uint64_t size; + + size = vm_kmem_offset(PMAP_MAX_KMEM_ADDRESS); + vm_object_init(&vm_kmem_kernel_object, size); +} + static int vm_kmem_alloc_check(size_t size) { @@ -98,6 +114,7 @@ vm_kmem_alloc(size_t size) { struct vm_page *page; uintptr_t va, start, end; + int error; size = vm_page_round(size); va = (uintptr_t)vm_kmem_alloc_va(size); @@ -110,30 +127,38 @@ vm_kmem_alloc(size_t size) page = vm_page_alloc(0, VM_PAGE_SEL_HIGHMEM, VM_PAGE_KERNEL); if (page == NULL) { - goto error_page; + goto error; } - pmap_enter(kernel_pmap, start, vm_page_to_pa(page), - VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); - } + /* + * The page becomes managed by the object and is freed in case + * of failure. + */ + error = vm_object_insert(&vm_kmem_kernel_object, page, + vm_kmem_offset(start)); - pmap_update(kernel_pmap); - return (void *)va; + if (error) { + goto error; + } -error_page: - size = start - va; + error = pmap_enter(kernel_pmap, start, vm_page_to_pa(page), + VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); - if (size != 0) { - pmap_update(kernel_pmap); - vm_kmem_free((void *)va, size); + if (error || (start - va == vm_page_ptoa(1000))) { + goto error; + } } - size = end - start; + error = pmap_update(kernel_pmap); - if (size != 0) { - vm_kmem_free_va((void *)start, size); + if (error) { + goto error; } + return (void *)va; + +error: + vm_kmem_free((void *)va, size); return NULL; } @@ -141,10 +166,7 @@ void vm_kmem_free(void *addr, size_t size) { const struct cpumap *cpumap; - struct vm_page *page; uintptr_t va, end; - phys_addr_t pa; - int error; va = (uintptr_t)addr; size = vm_page_round(size); @@ -152,16 +174,14 @@ vm_kmem_free(void *addr, size_t size) cpumap = cpumap_all(); while (va < end) { - error = pmap_kextract(va, &pa); - assert(!error); pmap_remove(kernel_pmap, va, cpumap); - page = vm_page_lookup(pa); - assert(page != NULL); - vm_page_free(page, 0); va += PAGE_SIZE; } pmap_update(kernel_pmap); + vm_object_remove(&vm_kmem_kernel_object, + vm_kmem_offset((uintptr_t)addr), + vm_kmem_offset(end)); vm_kmem_free_va(addr, size); } @@ -172,6 +192,7 @@ vm_kmem_map_pa(phys_addr_t pa, size_t size, uintptr_t offset, map_va; size_t map_size; phys_addr_t start; + int error; start = vm_page_trunc(pa); map_size = vm_page_round(pa + size) - start; @@ -181,11 +202,20 @@ vm_kmem_map_pa(phys_addr_t pa, size_t size, return NULL; } - for (offset = 0; offset < map_size; offset += PAGE_SIZE) - pmap_enter(kernel_pmap, map_va + offset, start + offset, - VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); + for (offset = 0; offset < map_size; offset += PAGE_SIZE) { + error = pmap_enter(kernel_pmap, map_va + offset, start + offset, + VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); - pmap_update(kernel_pmap); + if (error) { + goto error; + } + } + + error = pmap_update(kernel_pmap); + + if (error) { + goto error; + } if (map_vap != NULL) { *map_vap = map_va; @@ -196,6 +226,10 @@ vm_kmem_map_pa(phys_addr_t pa, size_t size, } return (void *)(map_va + (uintptr_t)(pa & PAGE_MASK)); + +error: + vm_kmem_unmap_pa(map_va, map_size); + return NULL; } void diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index 3628a69..d845c1d 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -44,6 +44,11 @@ extern char _end; */ extern struct vm_map *kernel_map; +/* + * Initialize the vm_kmem module. + */ +void vm_kmem_setup(void); + /* * Allocate pure virtual kernel pages. * diff --git a/vm/vm_setup.c b/vm/vm_setup.c index 77f5fdb..9fb9c52 100644 --- a/vm/vm_setup.c +++ b/vm/vm_setup.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ vm_setup(void) rdxtree_setup(); vm_object_setup(); vm_map_setup(); + vm_kmem_setup(); pmap_setup(); percpu_setup(); } -- cgit v1.2.3 From 634e07dad2dd596610908f7cdd6b82fcf0f35d0a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 2 Jul 2017 15:07:48 +0200 Subject: test/test_x86_double_fault: remove test --- Makefrag.am | 4 ---- configure.ac | 3 --- test/test_x86_double_fault.c | 28 ---------------------------- 3 files changed, 35 deletions(-) delete mode 100644 test/test_x86_double_fault.c diff --git a/Makefrag.am b/Makefrag.am index 47b4295..36e751d 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -157,10 +157,6 @@ if TEST_VM_PAGE_FILL x15_SOURCES += test/test_vm_page_fill.c endif TEST_VM_PAGE_FILL -if TEST_X86_DOUBLE_FAULT -x15_SOURCES += test/test_x86_double_fault.c -endif TEST_X86_DOUBLE_FAULT - if TEST_XCALL x15_SOURCES += test/test_xcall.c endif TEST_XCALL diff --git a/configure.ac b/configure.ac index 57f07d1..88a00a6 100644 --- a/configure.ac +++ b/configure.ac @@ -79,7 +79,6 @@ m4_define([ENABLE_TEST_MODULE], [sref_noref], [test_sref_noref=yes], [sref_weakref], [test_sref_weakref=yes], [vm_page_fill], [test_vm_page_fill=yes], - [x86_double_fault], [test_x86_double_fault=yes], [xcall], [test_xcall=yes], [AC_MSG_ERROR([invalid test module])]) AC_DEFINE([X15_RUN_TEST_MODULE], [1], @@ -101,8 +100,6 @@ AM_CONDITIONAL([TEST_SREF_WEAKREF], [test x"$test_sref_weakref" = xyes]) AM_CONDITIONAL([TEST_VM_PAGE_FILL], [test x"$test_vm_page_fill" = xyes]) -AM_CONDITIONAL([TEST_X86_DOUBLE_FAULT], - [test x"$test_x86_double_fault" = xyes]) AM_CONDITIONAL([TEST_XCALL], [test x"$test_xcall" = xyes]) diff --git a/test/test_x86_double_fault.c b/test/test_x86_double_fault.c deleted file mode 100644 index 20596fe..0000000 --- a/test/test_x86_double_fault.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 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 3 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 . - * - * - * Cause a double fault, see that it's correctly handled. - */ - -#include -#include - -void -test_setup(void) -{ - trap_trigger_double_fault(); -} -- cgit v1.2.3 From 7bded8776d3b23b79b7e54253e0818466043295a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 8 Jul 2017 20:54:16 +0200 Subject: Add a .gdbinit configuration file --- .gdbinit | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gdbinit diff --git a/.gdbinit b/.gdbinit new file mode 100644 index 0000000..f823e0a --- /dev/null +++ b/.gdbinit @@ -0,0 +1,3 @@ +define x15_connect_qemu + target remote localhost:1234 +end -- cgit v1.2.3 From cacd797c0c1825301f21aab18a7ce2c410d14535 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Jul 2017 20:05:07 +0200 Subject: kern/init: introduce initialization operations Init operations are a mechanism to run initilization functions based on their dependencies. They are meant to replace the imperatively declared static order currently used. --- Makefrag.am | 2 + kern/init.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/init.h | 68 +++++++++- kern/init_i.h | 63 +++++++++ 4 files changed, 541 insertions(+), 1 deletion(-) create mode 100644 kern/init.c create mode 100644 kern/init_i.h diff --git a/Makefrag.am b/Makefrag.am index 36e751d..6f4e6d4 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -31,7 +31,9 @@ x15_SOURCES += \ kern/hash.h \ kern/fmt.c \ kern/fmt.h \ + kern/init.c \ kern/init.h \ + kern/init_i.h \ kern/intr.c \ kern/intr.h \ kern/kernel.c \ diff --git a/kern/init.c b/kern/init.c new file mode 100644 index 0000000..1dc5114 --- /dev/null +++ b/kern/init.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * The main purpose of the init module is to provide a convenient interface + * to declare initialization operations and their dependency, infer an order + * of execution based on the resulting graph (which must be acyclic), and + * run the operations in that order. Topological sorting is achieved using + * Kahn's algorithm. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define INIT_DEBUG 1 + +extern struct init_op _init_ops; +extern struct init_op _init_ops_end; + +static_assert(sizeof(struct init_op) == INIT_OP_ALIGN, "invalid init_op size"); + +/* + * List of initialization operations. + * + * This type is used for the output of the topological sort. + */ +struct init_ops_list { + struct list ops; + size_t size; +}; + +static void __init +init_ops_list_init(struct init_ops_list *list) +{ + list_init(&list->ops); + list->size = 0; +} + +static size_t __init +init_ops_list_size(const struct init_ops_list *list) +{ + return list->size; +} + +static void __init +init_ops_list_push(struct init_ops_list *list, struct init_op *op) +{ + list_insert_tail(&list->ops, &op->list_node); + list->size++; +} + +static struct init_op * __init +init_ops_list_pop(struct init_ops_list *list) +{ + struct init_op *op; + + if (list->size == 0) { + return NULL; + } + + op = list_last_entry(&list->ops, struct init_op, list_node); + list_remove(&op->list_node); + list->size--; + return op; +} + +/* + * Stack of initialization operations. + * + * This type is used internally by the topological sort algorithm. + */ +struct init_ops_stack { + struct list ops; +}; + +static void __init +init_ops_stack_init(struct init_ops_stack *stack) +{ + list_init(&stack->ops); +} + +static void __init +init_ops_stack_push(struct init_ops_stack *stack, struct init_op *op) +{ + list_insert_tail(&stack->ops, &op->stack_node); +} + +static struct init_op * __init +init_ops_stack_pop(struct init_ops_stack *stack) +{ + struct init_op *op; + + if (list_empty(&stack->ops)) { + return NULL; + } + + op = list_last_entry(&stack->ops, struct init_op, stack_node); + list_remove(&op->stack_node); + return op; +} + +static struct init_op_dep * __init +init_op_get_dep(const struct init_op *op, size_t index) +{ + assert(index < op->nr_deps); + return &op->deps[index]; +} + +static void __init +init_op_init(struct init_op *op) +{ + struct init_op_dep *dep; + + for (size_t i = 0; i < op->nr_deps; i++) { + dep = init_op_get_dep(op, i); + dep->op->nr_parents++; + assert(dep->op->nr_parents != 0); + } +} + +static bool __init +init_op_orphan(const struct init_op *op) +{ + return (op->nr_parents == 0); +} + +static bool __init +init_op_pending(const struct init_op *op) +{ + return op->state == INIT_OP_STATE_PENDING; +} + +static void __init +init_op_set_pending(struct init_op *op) +{ + assert(op->state == INIT_OP_STATE_UNLINKED); + op->state = INIT_OP_STATE_PENDING; +} + +static bool __init +init_op_complete(const struct init_op *op) +{ + return op->state == INIT_OP_STATE_COMPLETE; +} + +static void __init +init_op_set_complete(struct init_op *op) +{ + assert(init_op_pending(op)); + op->state = INIT_OP_STATE_COMPLETE; +} + +static bool __init +init_op_ready(const struct init_op *op) +{ + const struct init_op_dep *dep; + size_t i; + + for (i = 0; i < op->nr_deps; i++) { + dep = init_op_get_dep(op, i); + + if (!init_op_complete(dep->op) + || (dep->required && dep->op->error)) { + return false; + } + } + + return true; +} + +static void __init +init_op_run(struct init_op *op) +{ + if (init_op_ready(op)) { + op->error = op->fn(); + } + + init_op_set_complete(op); +} + +#if INIT_DEBUG + +#define INIT_DEBUG_LOG_BUFFER_SIZE 8192 + +struct init_debug_log { + char buffer[INIT_DEBUG_LOG_BUFFER_SIZE]; + size_t index; +}; + +/* + * Buffers used to store an easy-to-dump text representation of init operations. + * + * These buffers are meant to be retrieved through a debugging interface such + * as JTAG. + */ +struct init_debug_logs { + struct init_debug_log roots; /* graph roots */ + struct init_debug_log cycles; /* operations with dependency cycles */ + struct init_debug_log pending; /* operations successfully sorted */ + struct init_debug_log complete; /* executed operations */ +}; + +static struct init_debug_logs init_debug_logs; + +static void __init +init_debug_log_append(struct init_debug_log *log, const char *name) +{ + size_t size; + + if (log->index == sizeof(log->buffer)) { + return; + } + + size = sizeof(log->buffer) - log->index; + log->index += snprintf(log->buffer + log->index, size, "%s ", name); + + if (log->index >= sizeof(log->buffer)) { + log->index = sizeof(log->buffer); + } +} + +static void __init +init_debug_append_root(const struct init_op *op) +{ + init_debug_log_append(&init_debug_logs.roots, op->name); +} + +static void __init +init_debug_append_cycle(const struct init_op *op) +{ + init_debug_log_append(&init_debug_logs.cycles, op->name); +} + +static void __init +init_debug_append_pending(const struct init_op *op) +{ + init_debug_log_append(&init_debug_logs.pending, op->name); +} + +static void __init +init_debug_append_complete(const struct init_op *op) +{ + init_debug_log_append(&init_debug_logs.complete, op->name); +} + +static void __init +init_debug_scan_not_pending(void) +{ + const struct init_op *op; + + for (op = &_init_ops; op < &_init_ops_end; op++) { + if (!init_op_pending(op)) { + init_debug_append_cycle(op); + } + } +} + +#else /* INIT_DEBUG */ +#define init_debug_append_root(roots) +#define init_debug_append_pending(op) +#define init_debug_append_complete(op) +#define init_debug_scan_not_pending() +#endif /* INIT_DEBUG */ + +static void __init +init_add_pending_op(struct init_ops_list *pending_ops, struct init_op *op) +{ + assert(!init_op_pending(op)); + + init_op_set_pending(op); + init_ops_list_push(pending_ops, op); + init_debug_append_pending(op); +} + +static void __init +init_op_visit(struct init_op *op, struct init_ops_stack *stack) +{ + struct init_op_dep *dep; + + for (size_t i = 0; i < op->nr_deps; i++) { + dep = init_op_get_dep(op, i); + assert(dep->op->nr_parents != 0); + dep->op->nr_parents--; + + if (init_op_orphan(dep->op)) { + init_ops_stack_push(stack, dep->op); + } + } +} + +static void __init +init_check_ops_alignment(void) +{ + uintptr_t start, end; + + start = (uintptr_t)&_init_ops; + end = (uintptr_t)&_init_ops_end; + + if (((end - start) % INIT_OP_ALIGN) != 0) { + cpu_halt(); + } +} + +static void __init +init_bootstrap(void) +{ + struct init_op *op; + + init_check_ops_alignment(); + + for (op = &_init_ops; op < &_init_ops_end; op++) { + init_op_init(op); + } +} + +static void __init +init_scan_roots(struct init_ops_stack *stack) +{ + struct init_op *op; + + init_ops_stack_init(stack); + + for (op = &_init_ops; op < &_init_ops_end; op++) { + if (init_op_orphan(op)) { + init_ops_stack_push(stack, op); + init_debug_append_root(op); + } + } +} + +static void __init +init_scan_ops(struct init_ops_list *pending_ops) +{ + struct init_ops_stack stack; + struct init_op *op; + size_t nr_ops; + + init_scan_roots(&stack); + + for (;;) { + op = init_ops_stack_pop(&stack); + + if (op == NULL) { + break; + } + + init_add_pending_op(pending_ops, op); + init_op_visit(op, &stack); + } + + init_debug_scan_not_pending(); + + nr_ops = &_init_ops_end - &_init_ops; + + if (init_ops_list_size(pending_ops) != nr_ops) { + cpu_halt(); + } +} + +static void __init +init_run_ops(struct init_ops_list *pending_ops) +{ + struct init_op *op; + + for (;;) { + op = init_ops_list_pop(pending_ops); + + if (op == NULL) { + break; + } + + init_op_run(op); + init_debug_append_complete(op); + } +} + +void __init +init_setup(void) +{ + struct init_ops_list pending_ops; + + init_ops_list_init(&pending_ops); + + init_bootstrap(); + init_scan_ops(&pending_ops); + init_run_ops(&pending_ops); +} diff --git a/kern/init.h b/kern/init.h index 62fecf4..d15d950 100644 --- a/kern/init.h +++ b/kern/init.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012 Richard Braun. + * Copyright (c) 2010-2017 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 @@ -13,6 +13,11 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * + * Init sections and operations. + * + * TODO Make the x86 linker script use macros from this header. */ #ifndef _KERN_INIT_H @@ -25,8 +30,15 @@ #define INIT_SECTION .init.text #define INIT_DATA_SECTION .init.data +/* + * This section must only contain init operation structures, and must be + * located inside the .init section. + */ +#define INIT_OPS_SECTION .init.ops + #ifndef __ASSEMBLER__ +#include #include #define __init __section(QUOTE(INIT_SECTION)) @@ -38,6 +50,60 @@ extern char _init; extern char _init_end; +/* + * Type for initialization operation functions. + */ +typedef int (*init_op_fn_t)(void); + +#include + +/* + * Forge an init operation declaration. + */ +#define INIT_OP_DECLARE(fn) extern struct init_op INIT_OP(fn) + +/* + * Foge an entry suitable as an init operation dependency. + * + * If a dependency isn't required, it's still used to determine run + * order, but its result is ignored, and the operation depending on it + * behaves as if that dependency succeeded. + */ +#define INIT_OP_DEP(fn, required) { &INIT_OP(fn), required } + +/* + * Init operation definition macro. + * + * This macro is used to define a structure named after the given function. + * Init operations are placed in a specific section which doesn't contain + * any other object type, making it a system-wide array of init operations. + * There is no need to actively register init operations; this module finds + * them all from their section. Dependencies are given as a variable-length + * argument list of entries built with the INIT_OP_DEP() macro. + */ +#define INIT_OP_DEFINE(_fn, ...) \ + static struct init_op_dep INIT_OP_DEPS(_fn)[] __initdata = { \ + __VA_ARGS__ \ + }; \ + \ + struct init_op INIT_OP(_fn) __initop = { \ + .name = QUOTE(_fn), \ + .fn = _fn, \ + .deps = INIT_OP_DEPS(_fn), \ + .error = ERROR_AGAIN, \ + .state = INIT_OP_STATE_UNLINKED, \ + .nr_deps = ARRAY_SIZE(INIT_OP_DEPS(_fn)), \ + .nr_parents = 0, \ + } + +/* + * Initialize the init module. + * + * Scan the section containing init operations, resolve all dependencies, + * and run operations in an appropriate order. + */ +void init_setup(void); + #endif /* __ASSEMBLER__ */ #endif /* _KERN_INIT_H */ diff --git a/kern/init_i.h b/kern/init_i.h new file mode 100644 index 0000000..be23073 --- /dev/null +++ b/kern/init_i.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_INIT_I_H +#define _KERN_INIT_I_H + +/* + * Alignment is important to make sure initialization operations are + * stored as a C array in the reserved init op section. + */ +#define INIT_OP_ALIGN 64 + +#include +#include +#include + +#include +#include + +#define __initop __section(QUOTE(INIT_OPS_SECTION)) + +#define INIT_OP_STATE_UNLINKED 0 +#define INIT_OP_STATE_PENDING 1 +#define INIT_OP_STATE_COMPLETE 2 + +struct init_op { + alignas(INIT_OP_ALIGN) struct list list_node; + struct list stack_node; + const char *name; + init_op_fn_t fn; + struct init_op_dep *deps; + int error; + unsigned char state; + unsigned char nr_deps; + unsigned char nr_parents; +}; + +struct init_op_dep { + struct init_op *op; + bool required; +}; + +#define __INIT_OP_DEPS(fn) fn ## _init_op_deps +#define INIT_OP_DEPS(fn) __INIT_OP_DEPS(fn) + +#define __INIT_OP(fn) fn ## _init_op +#define INIT_OP(fn) __INIT_OP(fn) + +#endif /* _KERN_INIT_I_H */ -- cgit v1.2.3 From b2ad7d862388558556288877a65f2797528168f4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Jul 2017 20:07:07 +0200 Subject: Switch to initialization operations --- Makefrag.am | 4 +- arch/x86/machine/acpi.c | 29 ++++++++++-- arch/x86/machine/acpi.h | 12 +++-- arch/x86/machine/atcons.c | 17 +++++-- arch/x86/machine/atcons.h | 25 +++++----- arch/x86/machine/atkbd.c | 16 +++++-- arch/x86/machine/atkbd.h | 7 ++- arch/x86/machine/biosmem.c | 27 ++++++++--- arch/x86/machine/biosmem.h | 21 +++------ arch/x86/machine/boot.c | 112 ++++++++++++++++++++++++++------------------- arch/x86/machine/boot.h | 42 +++++++++++++++++ arch/x86/machine/cga.c | 8 ++-- arch/x86/machine/cga.h | 11 +++-- arch/x86/machine/cpu.c | 58 +++++++++++++++-------- arch/x86/machine/cpu.h | 44 ++++++++++-------- arch/x86/machine/pmap.c | 42 +++++++++++------ arch/x86/machine/pmap.h | 31 +++++++------ arch/x86/machine/strace.c | 23 +++++++++- arch/x86/machine/strace.h | 14 ++++-- arch/x86/machine/tcb.c | 9 ++++ arch/x86/machine/tcb.h | 7 +++ arch/x86/machine/trap.c | 6 ++- arch/x86/machine/trap.h | 13 ++++-- arch/x86/machine/uart.c | 48 ++++++++++++------- arch/x86/machine/uart.h | 21 ++++----- arch/x86/x15.lds.S | 6 +++ kern/arg.c | 24 +++++----- kern/arg.h | 14 +++++- kern/console.c | 20 +++++++- kern/console.h | 19 ++++++-- kern/cpumap.c | 9 +++- kern/cpumap.h | 13 ++++-- kern/intr.c | 24 +++++++++- kern/intr.h | 20 ++++++-- kern/kernel.c | 38 +-------------- kern/kmem.c | 33 +++++++++++-- kern/kmem.h | 22 +++++---- kern/llsync.c | 14 +++++- kern/llsync.h | 5 -- kern/log.c | 36 +++++++++++++-- kern/log.h | 21 +++++---- kern/macros.h | 10 ++++ kern/mutex.c | 11 ++++- kern/mutex.h | 9 ++++ kern/panic.c | 12 +++++ kern/panic.h | 8 ++++ kern/percpu.c | 22 +++++++-- kern/percpu.h | 32 +++++-------- kern/printf.c | 10 +++- kern/printf.h | 8 +++- kern/rdxtree.c | 7 ++- kern/rdxtree.h | 6 ++- kern/shell.c | 10 +++- kern/shell.h | 15 +++--- kern/shutdown.c | 31 +++++++++++-- kern/shutdown.h | 18 ++++++-- kern/sleepq.c | 13 +++++- kern/sleepq.h | 27 ++++++----- kern/spinlock.c | 10 ++++ kern/spinlock.h | 9 ++++ kern/sref.c | 21 ++++++++- kern/sref.h | 14 ------ kern/syscnt.c | 30 +++++++----- kern/syscnt.h | 20 +++----- kern/task.c | 23 ++++++++-- kern/task.h | 13 ++++-- kern/thread.c | 80 ++++++++++++++++++++++++++------ kern/thread.h | 33 ++++++++----- kern/turnstile.c | 13 +++++- kern/turnstile.h | 28 ++++++------ kern/work.c | 14 +++++- kern/work.h | 14 ++++-- kern/xcall.c | 10 +++- kern/xcall.h | 5 -- vm/vm_kmem.c | 10 +++- vm/vm_kmem.h | 12 +++-- vm/vm_map.c | 37 +++++++++++++-- vm/vm_map.h | 19 ++++++-- vm/vm_object.c | 8 +++- vm/vm_object.h | 12 +++-- vm/vm_page.c | 22 ++++++++- vm/vm_page.h | 22 ++++----- vm/vm_setup.c | 40 ---------------- vm/vm_setup.h | 28 ------------ 84 files changed, 1171 insertions(+), 590 deletions(-) delete mode 100644 vm/vm_setup.c delete mode 100644 vm/vm_setup.h diff --git a/Makefrag.am b/Makefrag.am index 6f4e6d4..554a8b5 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -124,9 +124,7 @@ x15_SOURCES += \ vm/vm_object_types.h \ vm/vm_page.c \ vm/vm_page.h \ - vm/vm_prot.h \ - vm/vm_setup.c \ - vm/vm_setup.h + vm/vm_prot.h x15_SOURCES += \ test/test.h diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 93dc325..dabc7f3 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -22,10 +22,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -661,7 +663,7 @@ acpi_load_fadt(void) shutdown_register(&acpi_shutdown_ops, ACPI_SHUTDOWN_PRIORITY); } -int __init +static int __init acpi_setup(void) { struct acpi_rsdp rsdp; @@ -670,18 +672,39 @@ acpi_setup(void) error = acpi_find_rsdp(&rsdp); if (error) { - return error; + goto error; } error = acpi_copy_tables(&rsdp); if (error) { - return error; + goto error; } acpi_info(); acpi_load_madt(); acpi_load_fadt(); acpi_free_tables(); + + return 0; + +error: + /* + * For the sake of simplicity, it has been decided to ignore legacy + * specifications such as the multiprocessor specification, and use + * ACPI only. If ACPI is unavailable, consider the APIC system to + * be missing and fall back to using the legaxy XT-PIC. + */ + pic_setup(); return 0; } + +INIT_OP_DEFINE(acpi_setup, + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(intr_bootstrap, true), + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(shutdown_bootstrap, true), + INIT_OP_DEP(trap_setup, true), + INIT_OP_DEP(vm_kmem_setup, true)); diff --git a/arch/x86/machine/acpi.h b/arch/x86/machine/acpi.h index b767b9a..1f65fd6 100644 --- a/arch/x86/machine/acpi.h +++ b/arch/x86/machine/acpi.h @@ -22,12 +22,14 @@ #ifndef _X86_ACPI_H #define _X86_ACPI_H +#include + /* - * Load ACPI information. - * - * Return 0 if successful (an error usually means hardware doesn't support - * ACPI). + * This init operation provides : + * - Multiprocessor probing + * - registration of I/O APIC interrupt controllers + * - registration of ACPI shutdown operations */ -int acpi_setup(void); +INIT_OP_DECLARE(acpi_setup); #endif /* _X86_ACPI_H */ diff --git a/arch/x86/machine/atcons.c b/arch/x86/machine/atcons.c index e1e14c6..0c35266 100644 --- a/arch/x86/machine/atcons.c +++ b/arch/x86/machine/atcons.c @@ -129,21 +129,28 @@ static const struct console_ops atcons_ops = { .putc = atcons_putc, }; -void __init +static int __init atcons_bootstrap(void) { - cga_setup(); - console_init(&atcons_console, "atcons", &atcons_ops); console_register(&atcons_console); + return 0; } -void __init +INIT_OP_DEFINE(atcons_bootstrap, + INIT_OP_DEP(cga_setup, true), + INIT_OP_DEP(console_bootstrap, true)); + +static int __init atcons_setup(void) { - atkbd_setup(); + return 0; } +INIT_OP_DEFINE(atcons_setup, + INIT_OP_DEP(atcons_bootstrap, true), + INIT_OP_DEP(atkbd_setup, true)); + void atcons_intr(const char *s) { diff --git a/arch/x86/machine/atcons.h b/arch/x86/machine/atcons.h index d6923df..0d4893a 100644 --- a/arch/x86/machine/atcons.h +++ b/arch/x86/machine/atcons.h @@ -21,17 +21,7 @@ #ifndef _X86_ATCONS_H #define _X86_ATCONS_H -/* - * Early initialization of the atcons module. - */ -void atcons_bootstrap(void); - -/* - * Initialize the atcons module. - * - * This function enables keyboard interrupt handling. - */ -void atcons_setup(void); +#include /* * Console interrupt handler. @@ -49,4 +39,17 @@ void atcons_bottom(void); void atcons_right(void); void atcons_up(void); +/* + * This init operation provides : + * - CGA output through the console module + */ +INIT_OP_DECLARE(atcons_bootstrap); + +/* + * This init operation provides : + * - AT keyboard input through the console module + * - module fully initialized + */ +INIT_OP_DECLARE(atcons_setup); + #endif /* _X86_ATCONS_H */ diff --git a/arch/x86/machine/atkbd.c b/arch/x86/machine/atkbd.c index fd80426..59f8d68 100644 --- a/arch/x86/machine/atkbd.c +++ b/arch/x86/machine/atkbd.c @@ -843,7 +843,7 @@ atkbd_intr(void *arg) return 0; } -void __init +static int __init atkbd_setup(void) { int error; @@ -851,25 +851,31 @@ atkbd_setup(void) error = atkbd_flush(); if (error) { - return; + return 0; } error = atkbd_disable(); if (error) { - return; + return 0; } error = intr_register(ATKBD_INTR1, atkbd_intr, NULL); if (error) { log_err("atkbd: unable to register interrupt handler"); - return; + return 0; } error = atkbd_enable(); if (error) { - return; + return 0; } + + return 0; } + +INIT_OP_DEFINE(atkbd_setup, + INIT_OP_DEP(atcons_bootstrap, true), + INIT_OP_DEP(intr_setup, true)); diff --git a/arch/x86/machine/atkbd.h b/arch/x86/machine/atkbd.h index 0bed7ad..9efeefb 100644 --- a/arch/x86/machine/atkbd.h +++ b/arch/x86/machine/atkbd.h @@ -21,9 +21,12 @@ #ifndef _X86_ATKBD_H #define _X86_ATKBD_H +#include + /* - * Initialize the atkbd module. + * This init operation provides : + * - module fully initialized */ -void atkbd_setup(void); +INIT_OP_DECLARE(atkbd_setup); #endif /* _X86_ATKBD_H */ diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index 64482c6..fc03614 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #define BIOSMEM_MAX_BOOT_DATA 64 @@ -234,9 +233,9 @@ biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end) 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)); + memmove(&biosmem_boot_data_array[i], + &biosmem_boot_data_array[i + 1], + (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array)); } static void __boot @@ -813,7 +812,7 @@ biosmem_load_zone(struct biosmem_zone *zone, uint64_t max_phys_end) if (phys_end > max_phys_end) { if (max_phys_end <= phys_start) { log_warning("biosmem: zone %s physically unreachable, " - "not loaded", vm_page_zone_name(zone_index)); + "not loaded", vm_page_zone_name(zone_index)); return; } @@ -847,7 +846,7 @@ biosmem_load_zone(struct biosmem_zone *zone, uint64_t max_phys_end) } } -void __init +static int __init biosmem_setup(void) { uint64_t max_phys_end; @@ -870,8 +869,14 @@ biosmem_setup(void) zone = &biosmem_zones[i]; biosmem_load_zone(zone, max_phys_end); } + + return 0; } +INIT_OP_DEFINE(biosmem_setup, + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(log_setup, true)); + static void __init biosmem_unregister_temporary_boot_data(void) { @@ -925,7 +930,7 @@ biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end) } } -void __init +static int __init biosmem_free_usable(void) { struct biosmem_map_entry *entry; @@ -963,4 +968,12 @@ biosmem_free_usable(void) biosmem_free_usable_entry(start, end); } + + return 0; } + +INIT_OP_DEFINE(biosmem_free_usable, + INIT_OP_DEP(boot_save_data, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(vm_page_setup, true)); diff --git a/arch/x86/machine/biosmem.h b/arch/x86/machine/biosmem.h index d877af4..2a701de 100644 --- a/arch/x86/machine/biosmem.h +++ b/arch/x86/machine/biosmem.h @@ -20,6 +20,7 @@ #include +#include #include #include @@ -53,9 +54,9 @@ * 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. + * If the range is marked temporary, it will be unregistered once + * the boot data have been saved/consumed so that their backing + * pages are loaded into the VM system. * * This function is called before paging is enabled. */ @@ -102,17 +103,9 @@ const void * biosmem_get_bda(void); 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. + * This init operation provides : + * - heaps of physical memory are loaded by 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); +INIT_OP_DECLARE(biosmem_setup); #endif /* _X86_BIOSMEM_H */ diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 571b04f..25e92ff 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -43,27 +43,19 @@ */ #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 @@ -74,10 +66,8 @@ #include #include #include -#include #include #include -#include alignas(CPU_DATA_ALIGN) char boot_stack[BOOT_STACK_SIZE] __bootdata; alignas(CPU_DATA_ALIGN) char boot_ap_stack[BOOT_STACK_SIZE] __bootdata; @@ -358,8 +348,8 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) return pmap_setup_paging(); } -static void __init -boot_log_version(void) +void __init +boot_log_info(void) { log_info(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION #ifdef X15_X86_PAE @@ -480,46 +470,24 @@ boot_save_mods(void) * the boot loader. Once the boot data are managed as kernel buffers, their * backing pages can be freed. */ -static void __init +static int __init boot_save_data(void) { boot_mbi.flags = boot_raw_mbi.flags; boot_save_mods(); - strace_setup(&boot_raw_mbi); + return 0; } +INIT_OP_DEFINE(boot_save_data, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(vm_kmem_setup, true)); + void __init boot_main(void) { - log_setup(); - arg_setup(boot_tmp_cmdline); - sleepq_bootstrap(); - turnstile_bootstrap(); - syscnt_setup(); - percpu_bootstrap(); - trap_setup(); - cpu_setup(); - thread_bootstrap(); - boot_log_version(); - arg_log_info(); - console_setup(); - atcons_bootstrap(); - uart_bootstrap(); - printf_setup(); - uart_info(); - pmap_bootstrap(); - sref_bootstrap(); - cpu_check(cpu_current()); - cpu_info(cpu_current()); - biosmem_setup(); - vm_setup(); - boot_save_data(); - biosmem_free_usable(); - shutdown_setup(); - intr_setup(); - cpu_mp_probe(); - atcons_setup(); - uart_setup(); + arg_set_cmdline(boot_tmp_cmdline); + strace_set_mbi(&boot_raw_mbi); kernel_main(); /* Never reached */ @@ -529,9 +497,61 @@ void __init boot_ap_main(void) { cpu_ap_setup(); - thread_ap_bootstrap(); - pmap_ap_bootstrap(); + thread_ap_setup(); + pmap_ap_setup(); kernel_ap_main(); /* Never reached */ } + +/* + * Init operation aliases. + */ + +static int __init +boot_bootstrap_console(void) +{ + return 0; +} + +INIT_OP_DEFINE(boot_bootstrap_console, + INIT_OP_DEP(atcons_bootstrap, true), + INIT_OP_DEP(uart_bootstrap, true)); + +static int __init +boot_setup_console(void) +{ + return 0; +} + +INIT_OP_DEFINE(boot_setup_console, + INIT_OP_DEP(atcons_setup, true), + INIT_OP_DEP(uart_setup, true)); + +static int __init +boot_load_vm_page_zones(void) +{ + return 0; +} + +INIT_OP_DEFINE(boot_load_vm_page_zones, + INIT_OP_DEP(biosmem_setup, true)); + +static int __init +boot_setup_intr(void) +{ + return 0; +} + +INIT_OP_DEFINE(boot_setup_intr, + INIT_OP_DEP(acpi_setup, true)); + +static int __init +boot_setup_shutdown(void) +{ + return 0; +} + +INIT_OP_DEFINE(boot_setup_shutdown, + INIT_OP_DEP(acpi_setup, true), + INIT_OP_DEP(cpu_setup_shutdown, true)); diff --git a/arch/x86/machine/boot.h b/arch/x86/machine/boot.h index 8c590c6..c85c264 100644 --- a/arch/x86/machine/boot.h +++ b/arch/x86/machine/boot.h @@ -60,6 +60,7 @@ #ifndef __ASSEMBLER__ +#include #include #include @@ -122,6 +123,47 @@ void boot_main(void); */ void boot_ap_main(void); +/* + * Log kernel version and other architecture-specific information. + */ +void boot_log_info(void); + +/* + * This init operation provides : + * - boot data are saved + */ +INIT_OP_DECLARE(boot_save_data); + +/* + * This init operation provides : + * - all console devices are bootstrapped + */ +INIT_OP_DECLARE(boot_bootstrap_console); + +/* + * This init operation provides : + * - all console devices are fully initialized + */ +INIT_OP_DECLARE(boot_setup_console); + +/* + * This init operation provides : + * - physical memory has been loaded to the VM system + */ +INIT_OP_DECLARE(boot_load_vm_page_zones); + +/* + * This init operation provides : + * - all interrupt controllers have been registered + */ +INIT_OP_DECLARE(boot_setup_intr); + +/* + * This init operation provides : + * - all shutdown operations have been registered + */ +INIT_OP_DECLARE(boot_setup_shutdown); + #endif /* __ASSEMBLER__ */ #endif /* _X86_BOOT_H */ diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index 3ce4032..b519ba1 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -20,10 +20,10 @@ #include #include +#include #include #include #include -#include #include #include #include @@ -448,15 +448,17 @@ cga_setup_misc_out(void) } } -void __init +static int __init cga_setup(void) { cga_memory = (void *)vm_page_direct_va(CGA_MEMORY); - cga_setup_misc_out(); cga_bbuf_init(&cga_bbuf, cga_get_cursor_position()); + return 0; } +INIT_OP_DEFINE(cga_setup); + void cga_cursor_left(void) { diff --git a/arch/x86/machine/cga.h b/arch/x86/machine/cga.h index 2310bc9..2708ec4 100644 --- a/arch/x86/machine/cga.h +++ b/arch/x86/machine/cga.h @@ -21,10 +21,7 @@ #ifndef _X86_CGA_H #define _X86_CGA_H -/* - * Initialize the cga module. - */ -void cga_setup(void); +#include /* * Append a character to the CGA screen. @@ -37,4 +34,10 @@ void cga_putc(char c); void cga_cursor_left(void); void cga_cursor_right(void); +/* + * This init operation provides : + * - module fully initialized + */ +INIT_OP_DECLARE(cga_setup); + #endif /* _X86_CGA_H */ diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index debacf0..98d3680 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -124,7 +124,7 @@ struct cpu cpu_desc __percpu; /* * Number of active processors. */ -unsigned int cpu_nr_active __read_mostly; +unsigned int cpu_nr_active __read_mostly = 1; /* * Processor frequency, assumed fixed and equal on all processors. @@ -557,7 +557,7 @@ cpu_measure_freq(void) cpu_freq = (end - start) / (1000000 / CPU_FREQ_CAL_DELAY); } -void __init +static int __init cpu_setup(void) { struct cpu *cpu; @@ -566,18 +566,23 @@ cpu_setup(void) cpu_preinit(cpu, 0, CPU_INVALID_APIC_ID); cpu->double_fault_stack = cpu_double_fault_stack; /* XXX */ cpu_init(cpu); - cpu_nr_active = 1; cpu_measure_freq(); + + return 0; } +INIT_OP_DEFINE(cpu_setup, + INIT_OP_DEP(percpu_bootstrap, true), + INIT_OP_DEP(trap_setup, true)); + static void __init cpu_panic_on_missing_feature(const char *feature) { panic("cpu: %s feature missing", feature); } -void __init +static void __init cpu_check(const struct cpu *cpu) { if (!(cpu->features2 & CPU_FEATURE2_FPU)) { @@ -597,8 +602,19 @@ cpu_check(const struct cpu *cpu) #endif } +static int __init +cpu_check_bsp(void) +{ + cpu_check(cpu_current()); + return 0; +} + +INIT_OP_DEFINE(cpu_check_bsp, + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(panic_setup, true)); + void -cpu_info(const struct cpu *cpu) +cpu_log_info(const struct cpu *cpu) { log_info("cpu%u: %s, type %u, family %u, model %u, stepping %u", cpu->id, cpu->vendor_id, cpu->type, cpu->family, cpu->model, @@ -657,30 +673,32 @@ static struct shutdown_ops cpu_shutdown_ops = { .reset = cpu_shutdown_reset, }; -void __init +static int __init cpu_mp_probe(void) { - int error; - - error = acpi_setup(); - - if (error) { - /* - * For the sake of simplicity, it has been decided to ignore legacy - * specifications such as the multiprocessor specification, and use - * ACPI only. If ACPI is unavailable, consider the APIC system to - * be missing and fall back to using the legaxy XT-PIC. - */ - pic_setup(); - } - log_info("cpu: %u processor(s) configured", cpu_count()); + return 0; +} + +INIT_OP_DEFINE(cpu_mp_probe, + INIT_OP_DEP(acpi_setup, true), + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(log_setup, true)); +static int __init +cpu_setup_shutdown(void) +{ if (cpu_count() == 1) { shutdown_register(&cpu_shutdown_ops, CPU_SHUTDOWN_PRIORITY); } + + return 0; } +INIT_OP_DEFINE(cpu_setup_shutdown, + INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(shutdown_bootstrap, true)); + void __init cpu_mp_setup(void) { diff --git a/arch/x86/machine/cpu.h b/arch/x86/machine/cpu.h index ee4eb18..39f17f8 100644 --- a/arch/x86/machine/cpu.h +++ b/arch/x86/machine/cpu.h @@ -127,6 +127,7 @@ #include #include +#include #include #include #include @@ -614,37 +615,22 @@ void * cpu_get_boot_stack(void); /* * Install an interrupt handler in the IDT. + * + * These functions may be called before the cpu module is initialized. */ void cpu_idt_set_gate(unsigned int vector, void (*isr)(void)); void cpu_idt_set_double_fault(void (*isr)(void)); /* - * Set up the cpu module. - */ -void cpu_setup(void); - -/* - * Make sure the CPU has some required features. - */ -void cpu_check(const struct cpu *cpu); - -/* - * Display processor information. + * Log processor information. */ -void cpu_info(const struct cpu *cpu); +void cpu_log_info(const struct cpu *cpu); /* * Register the presence of a local APIC. */ void cpu_mp_register_lapic(unsigned int apic_id, int is_bsp); -/* - * Probe application processors. - * - * On return, cpu_count() gives the actual number of managed processors. - */ -void cpu_mp_probe(void); - /* * Start application processors. * @@ -696,6 +682,26 @@ cpu_send_thread_schedule(unsigned int cpu) */ void cpu_thread_schedule_intr(struct trap_frame *frame); +/* + * This init operation provides : + * - initialization of the BSP structure. + * - cpu_delay() + * - cpu_local_ptr() and cpu_local_var() + */ +INIT_OP_DECLARE(cpu_setup); + +/* + * This init operation provides : + * - cpu_count() + */ +INIT_OP_DECLARE(cpu_mp_probe); + +/* + * This init operation provides : + * - cpu shutdown operations registered + */ +INIT_OP_DECLARE(cpu_setup_shutdown); + #endif /* __ASSEMBLER__ */ #endif /* _X86_CPU_H */ diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 93b6875..7ab70bf 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -828,7 +828,7 @@ pmap_syncer_init(struct pmap_syncer *syncer, unsigned int cpu) syscnt_register(&syncer->sc_update_protects, name); } -void __init +static int __init pmap_bootstrap(void) { struct pmap_cpu_table *cpu_table; @@ -864,20 +864,17 @@ pmap_bootstrap(void) if (cpu_has_global_pages()) { pmap_setup_global_pages(); } -} -void __init -pmap_ap_bootstrap(void) -{ - cpu_local_assign(pmap_current_ptr, kernel_pmap); - - if (cpu_has_global_pages()) { - cpu_enable_global_pages(); - } else { - cpu_tlb_flush(); - } + return 0; } +INIT_OP_DEFINE(pmap_bootstrap, + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(spinlock_setup, true), + INIT_OP_DEP(syscnt_setup, true), + INIT_OP_DEP(thread_bootstrap, true)); + static void __init pmap_setup_set_ptp_type(phys_addr_t ptp_pa, unsigned int index, unsigned int level) @@ -906,7 +903,7 @@ pmap_setup_fix_ptps(void) pmap_setup_set_ptp_type); } -void __init +static int __init pmap_setup(void) { pmap_setup_fix_ptps(); @@ -914,6 +911,25 @@ pmap_setup(void) kmem_cache_init(&pmap_update_oplist_cache, "pmap_update_oplist", sizeof(struct pmap_update_oplist), CPU_L1_SIZE, pmap_update_oplist_ctor, 0); + return 0; +} + +INIT_OP_DEFINE(pmap_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(pmap_bootstrap, true), + INIT_OP_DEP(vm_page_setup, true)); + +void __init +pmap_ap_setup(void) +{ + cpu_local_assign(pmap_current_ptr, kernel_pmap); + + if (cpu_has_global_pages()) { + cpu_enable_global_pages(); + } else { + cpu_tlb_flush(); + } } static void __init diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 8404258..114dcda 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -165,6 +165,7 @@ #include #include +#include #include #include #include @@ -203,22 +204,9 @@ pmap_pte_t * pmap_setup_paging(void); pmap_pte_t * pmap_ap_setup_paging(void); /* - * Early initialization of the pmap module. + * Initialize the pmap module on APs. */ -void pmap_bootstrap(void); - -/* - * Early initialization of the MMU on APs. - */ -void pmap_ap_bootstrap(void); - -/* - * Set up the pmap module. - * - * This function should only be called by the VM system, once kernel - * allocations can be performed safely. - */ -void pmap_setup(void); +void pmap_ap_setup(void); /* * Set up the pmap module for multiprocessor operations. @@ -330,6 +318,19 @@ pmap_current(void) return cpu_local_read(pmap_current_ptr); } +/* + * This init operation provides : + * - kernel pmap operations + */ +INIT_OP_DECLARE(pmap_bootstrap); + +/* + * This init operation provides : + * - user pmap creation + * - module fully initialized + */ +INIT_OP_DECLARE(pmap_setup); + #endif /* __ASSEMBLER__ */ #endif /* _X86_PMAP_H */ diff --git a/arch/x86/machine/strace.c b/arch/x86/machine/strace.c index 4b7e743..cfb237e 100644 --- a/arch/x86/machine/strace.c +++ b/arch/x86/machine/strace.c @@ -36,6 +36,8 @@ #define STRACE_ADDR_FORMAT "%#010lx" #endif /* __LP64__ */ +static const struct multiboot_raw_info *strace_mbi __initdata; + static struct elf_sym *strace_symtab __read_mostly; static struct elf_sym *strace_symtab_end __read_mostly; static char *strace_strtab __read_mostly; @@ -173,14 +175,23 @@ strace_lookup_section(const struct multiboot_raw_info *mbi, const void *table, } void __init -strace_setup(const struct multiboot_raw_info *mbi) +strace_set_mbi(const struct multiboot_raw_info *mbi) +{ + strace_mbi = mbi; +} + +static int __init +strace_setup(void) { const struct elf_shdr *shstrtab_hdr, *symtab_hdr, *strtab_hdr; + const struct multiboot_raw_info *mbi; uintptr_t map_addr, shstrtab_map_addr; size_t size, map_size, shstrtab_map_size; const char *shstrtab; const void *table; + mbi = strace_mbi; + if (!(mbi->flags & MULTIBOOT_LOADER_SHDR) || (mbi->shdr_num == 0)) { goto no_syms; } @@ -236,7 +247,7 @@ strace_setup(const struct multiboot_raw_info *mbi) vm_kmem_unmap_pa(shstrtab_map_addr, shstrtab_map_size); vm_kmem_unmap_pa(map_addr, map_size); - return; + return 0; error_strtab: kmem_free(strace_symtab, symtab_hdr->size); @@ -251,4 +262,12 @@ no_syms: strace_symtab = NULL; strace_symtab_end = NULL; strace_strtab = NULL; + return 0; } + +INIT_OP_DEFINE(strace_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(pmap_bootstrap, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(vm_kmem_setup, true)); diff --git a/arch/x86/machine/strace.h b/arch/x86/machine/strace.h index 66aa18b..2f686a0 100644 --- a/arch/x86/machine/strace.h +++ b/arch/x86/machine/strace.h @@ -21,6 +21,7 @@ #ifndef _X86_STRACE_H #define _X86_STRACE_H +#include #include #include @@ -45,10 +46,17 @@ strace_dump(void) } /* - * Setup the stack tracing module. + * Pass the multiboot information structure. * - * If available, the symbol table is extracted from the boot data. + * If available, the symbol table is extracted from the boot data, when + * the strace module is initialized. */ -void strace_setup(const struct multiboot_raw_info *mbi); +void strace_set_mbi(const struct multiboot_raw_info *mbi); + +/* + * This init operation provides : + * - module fully initialized + */ +INIT_OP_DECLARE(strace_setup); #endif /* _X86_STRACE_H */ diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index fb31a17..b54945c 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -104,3 +104,12 @@ tcb_trace(const struct tcb *tcb) { strace_show((uintptr_t)tcb_context_restore, tcb->bp); } + +static int __init +tcb_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(tcb_setup, + INIT_OP_DEP(cpu_setup, true)); diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 9c69a5e..9107f7c 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -95,4 +96,10 @@ tcb_switch(struct tcb *prev, struct tcb *next) */ void tcb_trace(const struct tcb *tcb); +/* + * This init operation provides : + * - current TCB handling + */ +INIT_OP_DECLARE(tcb_setup); + #endif /* _X86_TCB_H */ diff --git a/arch/x86/machine/trap.c b/arch/x86/machine/trap.c index 1580fa5..878f20c 100644 --- a/arch/x86/machine/trap.c +++ b/arch/x86/machine/trap.c @@ -171,7 +171,7 @@ trap_default(struct trap_frame *frame) cpu_halt(); } -void __init +static int __init trap_setup(void) { size_t i; @@ -213,8 +213,12 @@ trap_setup(void) trap_install(TRAP_LAPIC_TIMER, TRAP_HF_INTR, lapic_timer_intr); trap_install(TRAP_LAPIC_ERROR, TRAP_HF_INTR, lapic_error_intr); trap_install(TRAP_LAPIC_SPURIOUS, TRAP_HF_INTR, lapic_spurious_intr); + + return 0; } +INIT_OP_DEFINE(trap_setup); + void trap_main(struct trap_frame *frame) { diff --git a/arch/x86/machine/trap.h b/arch/x86/machine/trap.h index f8d0a16..699f19e 100644 --- a/arch/x86/machine/trap.h +++ b/arch/x86/machine/trap.h @@ -77,6 +77,7 @@ #include #include +#include #include #ifdef __LP64__ @@ -143,11 +144,6 @@ trap_trigger_double_fault(void) asm volatile("movl $0xdead, %esp; push $0"); } -/* - * Set up the trap module. - */ -void trap_setup(void); - /* * Unified trap entry point. */ @@ -177,6 +173,13 @@ void trap_stack_show(struct trap_frame *frame); */ void * trap_get_interrupt_stack(const struct trap_frame *frame); +/* + * This init operation provides : + * - initialization of all IDT entries and trap handlers + * - double fault exception support + */ +INIT_OP_DECLARE(trap_setup); + #endif /* __ASSEMBLER__ */ #endif /* _X86_TRAP_H */ diff --git a/arch/x86/machine/uart.c b/arch/x86/machine/uart.c index cdb49a9..10a3288 100644 --- a/arch/x86/machine/uart.c +++ b/arch/x86/machine/uart.c @@ -407,7 +407,23 @@ uart_init(struct uart *uart, uint16_t port, uint16_t intr) console_register(&uart->console); } -void __init +static void __init +uart_log_info(void) +{ + const struct uart *uart; + size_t i; + + for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { + uart = uart_get_dev(i); + + if (uart->port != 0) { + log_info("uart%zu: port:%#x irq:%u", i, (unsigned int)uart->port, + (unsigned int)uart->intr); + } + } +} + +static int __init uart_bootstrap(void) { const uint16_t *ptr; @@ -422,9 +438,17 @@ uart_bootstrap(void) uart_init(uart_get_dev(i), ptr[i], uart_intrs[i]); } + + uart_log_info(); + return 0; } -void __init +INIT_OP_DEFINE(uart_bootstrap, + INIT_OP_DEP(arg_setup, true), + INIT_OP_DEP(console_bootstrap, true), + INIT_OP_DEP(log_setup, true)); + +static int __init uart_setup(void) { struct uart *uart; @@ -439,20 +463,10 @@ uart_setup(void) uart_enable_intr(uart); } -} -void __init -uart_info(void) -{ - const struct uart *uart; - size_t i; - - for (i = 0; i < ARRAY_SIZE(uart_devs); i++) { - uart = uart_get_dev(i); - - if (uart->port != 0) { - log_info("uart%zu: port:%#x irq:%u", i, (unsigned int)uart->port, - (unsigned int)uart->intr); - } - } + return 0; } + +INIT_OP_DEFINE(uart_setup, + INIT_OP_DEP(intr_setup, true), + INIT_OP_DEP(uart_bootstrap, true)); diff --git a/arch/x86/machine/uart.h b/arch/x86/machine/uart.h index 60b195c..0054da8 100644 --- a/arch/x86/machine/uart.h +++ b/arch/x86/machine/uart.h @@ -21,24 +21,19 @@ #ifndef _X86_UART_H #define _X86_UART_H -/* - * Early initialization of the uart module. - * - * Devices may only be used to report diagnostics until initialization - * is completed. - */ -void uart_bootstrap(void); +#include /* - * Initialize the uart module. - * - * On return, devices may be used for both input and output, using interrupts. + * This init operation provides : + * - UART output through the console module */ -void uart_setup(void); +INIT_OP_DECLARE(uart_bootstrap); /* - * Display device information. + * This init operation provides : + * - UART input through the console module + * - module fully initialized */ -void uart_info(void); +INIT_OP_DECLARE(uart_setup); #endif /* _X86_UART_H */ diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index 0bf809b..352ebf4 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -44,6 +44,12 @@ SECTIONS .init ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.init))) { *(.init.text) *(.init.data) + + . = ALIGN(64); + _init_ops = .; + *(.init.ops) + _init_ops_end = .; + } : init . = ALIGN(PAGE_SIZE); diff --git a/kern/arg.c b/kern/arg.c index 62ace8b..7bb616a 100644 --- a/kern/arg.c +++ b/kern/arg.c @@ -35,22 +35,17 @@ static char arg_cmdline[ARG_CMDLINE_MAX_SIZE] __initdata; static const char *arg_cmdline_end __initdata; void __init -arg_setup(const char *cmdline) +arg_set_cmdline(const char *cmdline) { - size_t i, length; - - if (cmdline == NULL) { - arg_cmdline[0] = '\0'; - return; - } - - length = strlen(cmdline); + strlcpy(arg_cmdline, cmdline, sizeof(arg_cmdline)); +} - if ((length + 1) > ARRAY_SIZE(arg_cmdline)) { - panic("arg: command line too long"); - } +static int __init +arg_setup(void) +{ + size_t i, length; - memcpy(arg_cmdline, cmdline, length + 1); + length = strlen(arg_cmdline); for (i = 0; i < length; i++) { if (arg_cmdline[i] == ' ') { @@ -59,8 +54,11 @@ arg_setup(const char *cmdline) } arg_cmdline_end = arg_cmdline + length; + return 0; } +INIT_OP_DEFINE(arg_setup); + void __init arg_log_info(void) { diff --git a/kern/arg.h b/kern/arg.h index 3a8d20b..c7b70a3 100644 --- a/kern/arg.h +++ b/kern/arg.h @@ -27,12 +27,16 @@ #include +#include + #define ARG_CMDLINE_MAX_SIZE 256 /* - * Initialize the arg module. + * Set the command line string. + * + * This function must be called before calling the kernel main entry point. */ -void arg_setup(const char *cmdline); +void arg_set_cmdline(const char *cmdline); /* * Log command line information. @@ -54,4 +58,10 @@ bool arg_present(const char *name); */ const char * arg_value(const char *name); +/* + * This init operation provides : + * - command line arguments can be retrieved + */ +INIT_OP_DECLARE(arg_setup); + #endif /* _KERN_ARG_H */ diff --git a/kern/console.c b/kern/console.c index e026ffd..1f9a8a9 100644 --- a/kern/console.c +++ b/kern/console.c @@ -29,6 +29,7 @@ #include #include #include +#include #include /* @@ -129,13 +130,28 @@ out: return c; } -void __init -console_setup(void) +static int __init +console_bootstrap(void) { list_init(&console_devs); console_name = arg_value("console"); + return 0; +} + +INIT_OP_DEFINE(console_bootstrap, + INIT_OP_DEP(arg_setup, true), + INIT_OP_DEP(log_setup, true)); + +static int __init +console_setup(void) +{ + return 0; } +INIT_OP_DEFINE(console_setup, + INIT_OP_DEP(boot_setup_console, true), + INIT_OP_DEP(thread_setup, true)); + void __init console_register(struct console *console) { diff --git a/kern/console.h b/kern/console.h index a5aa478..1578997 100644 --- a/kern/console.h +++ b/kern/console.h @@ -22,6 +22,7 @@ #define _KERN_CONSOLE_H #include +#include #include #include #include @@ -61,11 +62,6 @@ struct console { void console_init(struct console *console, const char *name, const struct console_ops *ops); -/* - * Initialize the console module. - */ -void console_setup(void); - /* * Register a console device. * @@ -95,4 +91,17 @@ void console_intr(struct console *console, const char *s); void console_putchar(char c); char console_getchar(void); +/* + * This init operation provides : + * - registration of consoles + */ +INIT_OP_DECLARE(console_bootstrap); + +/* + * This init operation provides : + * - all consoles have been registered + * - module fully initialized + */ +INIT_OP_DECLARE(console_setup); + #endif /* _KERN_CONSOLE_H */ diff --git a/kern/cpumap.c b/kern/cpumap.c index e4ae864..d166b23 100644 --- a/kern/cpumap.c +++ b/kern/cpumap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ static struct cpumap cpumap_active_cpus __read_mostly = { { 1 } }; static struct kmem_cache cpumap_cache; -void +static int __init cpumap_setup(void) { unsigned int i, nr_cpus; @@ -40,8 +41,14 @@ cpumap_setup(void) for (i = 0; i < nr_cpus; i++) { cpumap_set(&cpumap_active_cpus, i); } + + return 0; } +INIT_OP_DEFINE(cpumap_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(cpu_mp_probe, true)); + const struct cpumap * cpumap_all(void) { diff --git a/kern/cpumap.h b/kern/cpumap.h index 1843a99..fd07afc 100644 --- a/kern/cpumap.h +++ b/kern/cpumap.h @@ -27,6 +27,7 @@ #define _KERN_CPUMAP_H #include +#include struct cpumap { BITMAP_DECLARE(cpus, X15_MAX_CPUS); @@ -134,11 +135,6 @@ cpumap_find_first_zero(const struct cpumap *cpumap) #define cpumap_for_each_zero(cpumap, index) \ bitmap_for_each_zero((cpumap)->cpus, X15_MAX_CPUS, index) -/* - * Initialize the cpumap module. - */ -void cpumap_setup(void); - /* * Return a cpumap representing all active processors. * @@ -167,4 +163,11 @@ void cpumap_destroy(struct cpumap *cpumap); */ int cpumap_check(const struct cpumap *cpumap); +/* + * This init operation provides : + * - cpumap creation + * - module fully initialized + */ +INIT_OP_DECLARE(cpumap_setup); + #endif /* _KERN_CPUMAP_H */ diff --git a/kern/intr.c b/kern/intr.c index 769f4d9..8579b74 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -315,8 +316,8 @@ intr_get_entry(unsigned int intr) return &intr_table[intr]; } -void __init -intr_setup(void) +static int __init +intr_bootstrap(void) { unsigned int i; @@ -328,8 +329,27 @@ intr_setup(void) for (i = 0; i < ARRAY_SIZE(intr_table); i++) { intr_entry_init(intr_get_entry(i)); } + + return 0; } +INIT_OP_DEFINE(intr_bootstrap, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(spinlock_setup, true), + INIT_OP_DEP(thread_bootstrap, true)); + +static int __init +intr_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(intr_setup, + INIT_OP_DEP(boot_setup_intr, true), + INIT_OP_DEP(intr_bootstrap, true)); + static void __init intr_check_range(unsigned int first_intr, unsigned int last_intr) { diff --git a/kern/intr.h b/kern/intr.h index 1cdab5e..ac3f8cf 100644 --- a/kern/intr.h +++ b/kern/intr.h @@ -21,6 +21,8 @@ #ifndef _KERN_INTR_H #define _KERN_INTR_H +#include + /* * Type for interrupt handler functions. * @@ -41,11 +43,6 @@ struct intr_ops { void (*eoi)(void *priv, unsigned int intr); }; -/* - * Initialize the intr module. - */ -void intr_setup(void); - /* * Register an interrupt controller. * @@ -66,4 +63,17 @@ void intr_unregister(unsigned int intr, intr_handler_fn_t fn); */ void intr_handle(unsigned int intr); +/* + * This init operation provides : + * - registration of interrupt controllers and handlers + */ +INIT_OP_DECLARE(intr_bootstrap); + +/* + * This init operation provides : + * - all interrupt controllers have been registered + * - module fully initialized + */ +INIT_OP_DECLARE(intr_setup); + #endif /* _KERN_INTR_H */ diff --git a/kern/kernel.c b/kern/kernel.c index 6c562f4..434297d 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Richard Braun. + * Copyright (c) 2011-2017 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 @@ -15,53 +15,19 @@ * along with this program. If not, see . */ -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include #include -#ifdef X15_RUN_TEST_MODULE -#include -#endif /* X15_RUN_TEST_MODULE */ - void __init kernel_main(void) { assert(!cpu_intr_enabled()); - percpu_cleanup(); - shell_setup(); - syscnt_register_shell_cmds(); - cpumap_setup(); - xcall_setup(); - task_setup(); - sleepq_setup(); - turnstile_setup(); - thread_setup(); - work_setup(); - llsync_setup(); - sref_setup(); + init_setup(); vm_page_log_info(); - shutdown_register_shell_cmds(); - log_start(); - -#ifdef X15_RUN_TEST_MODULE - test_setup(); -#endif /* X15_RUN_TEST_MODULE */ /* * Enabling application processors is done late in the boot process for diff --git a/kern/kmem.c b/kern/kmem.c index 572df4f..d98be0d 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -1171,10 +1171,23 @@ static struct shell_cmd kmem_shell_cmds[] = { "display information about kernel memory and caches"), }; +static int __init +kmem_setup_shell(void) +{ + SHELL_REGISTER_CMDS(kmem_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(kmem_setup_shell, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(thread_setup, true)); + #endif /* X15_SHELL */ -void __init -kmem_setup(void) +static int __init +kmem_bootstrap(void) { struct kmem_cpu_pool_type *cpu_pool_type; char name[KMEM_NAME_SIZE]; @@ -1209,9 +1222,23 @@ kmem_setup(void) size <<= 1; } - SHELL_REGISTER_CMDS(kmem_shell_cmds); + return 0; } +INIT_OP_DEFINE(kmem_bootstrap, + INIT_OP_DEP(thread_bootstrap, true), + INIT_OP_DEP(vm_page_setup, true)); + +static int __init +kmem_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(kmem_setup, + INIT_OP_DEP(kmem_bootstrap, true), + INIT_OP_DEP(vm_kmem_setup, true)); + static inline size_t kmem_get_index(unsigned long size) { diff --git a/kern/kmem.h b/kern/kmem.h index 8f6c773..3296ddb 100644 --- a/kern/kmem.h +++ b/kern/kmem.h @@ -23,6 +23,8 @@ #include +#include + /* * Object cache. */ @@ -76,14 +78,6 @@ void kmem_cache_free(struct kmem_cache *cache, void *obj); */ void kmem_cache_info(struct kmem_cache *cache); -/* - * Set up the kernel memory allocator module. - * - * This function should only be called by the VM system. Once it returns, - * caches can be initialized. - */ -void kmem_setup(void); - /* * Allocate size bytes of uninitialized memory. */ @@ -106,4 +100,16 @@ void kmem_free(void *ptr, size_t size); */ void kmem_info(void); +/* + * This init operation provides : + * - allocation from caches backed by the page allocator + */ +INIT_OP_DECLARE(kmem_bootstrap); + +/* + * This init operation provides : + * - allocation from all caches + */ +INIT_OP_DECLARE(kmem_setup); + #endif /* _KERN_KMEM_H */ diff --git a/kern/llsync.c b/kern/llsync.c index 8ba87b6..c432fc6 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -49,6 +49,7 @@ #include #include #include +#include #include /* @@ -81,7 +82,7 @@ llsync_ready(void) return llsync_is_ready; } -void __init +static int __init llsync_setup(void) { struct llsync_cpu_data *cpu_data; @@ -102,8 +103,19 @@ llsync_setup(void) cpu_data = percpu_ptr(llsync_cpu_data, i); work_queue_init(&cpu_data->queue0); } + + return 0; } +INIT_OP_DEFINE(llsync_setup, + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(spinlock_setup, true), + INIT_OP_DEP(syscnt_setup, true), + INIT_OP_DEP(thread_bootstrap, true), + INIT_OP_DEP(work_setup, true)); + static void llsync_process_global_checkpoint(void) { diff --git a/kern/llsync.h b/kern/llsync.h index 6736d6f..3852b0b 100644 --- a/kern/llsync.h +++ b/kern/llsync.h @@ -122,11 +122,6 @@ llsync_read_exit(void) */ bool llsync_ready(void); -/* - * Initialize the llsync module. - */ -void llsync_setup(void); - /* * Manage registration of the current processor. * diff --git a/kern/log.c b/kern/log.c index 96be152..0ddf679 100644 --- a/kern/log.c +++ b/kern/log.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #include #include #include +#include +#include #define LOG_BUFFER_SIZE 16384 @@ -396,18 +399,40 @@ static struct shell_cmd log_shell_cmds[] = { " 7: debug"), }; +static int __init +log_setup_shell(void) +{ + SHELL_REGISTER_CMDS(log_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(log_setup_shell, + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(shell_setup, true)); + #endif /* X15_SHELL */ -void __init +static int __init log_setup(void) { cbuf_init(&log_cbuf, log_buffer, sizeof(log_buffer)); log_index = cbuf_start(&log_cbuf); spinlock_init(&log_lock); log_print_level = LOG_INFO; + + boot_log_info(); + arg_log_info(); + cpu_log_info(cpu_current()); + + return 0; } -void __init +INIT_OP_DEFINE(log_setup, + INIT_OP_DEP(arg_setup, true), + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(spinlock_setup, true)); + +static int __init log_start(void) { struct thread_attr attr; @@ -421,9 +446,14 @@ log_start(void) panic("log: unable to create thread"); } - SHELL_REGISTER_CMDS(log_shell_cmds); + return 0; } +INIT_OP_DEFINE(log_start, + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(thread_setup, true)); + static void log_write(const void *s, size_t size) { diff --git a/kern/log.h b/kern/log.h index 003e99b..581a784 100644 --- a/kern/log.h +++ b/kern/log.h @@ -23,6 +23,8 @@ #include + +#include enum { LOG_EMERG, LOG_ALERT, @@ -35,16 +37,6 @@ enum { LOG_NR_LEVELS, }; -/* - * Initialize the log module. - */ -void log_setup(void); - -/* - * Start the log thread. - */ -void log_start(void); - /* * Generate a message and send it to the log thread. * @@ -92,4 +84,13 @@ int log_vinfo(const char *format, va_list ap) int log_vdebug(const char *format, va_list ap) __attribute__((format(printf, 1, 0))); +/* + * This init operation provides : + * - message logging + * + * The log thread isn't yet started and messages are merely stored in an + * internal buffer. + */ +INIT_OP_DECLARE(log_setup); + #endif /* _KERN_LOG_H */ diff --git a/kern/macros.h b/kern/macros.h index 6259dce..6b203e5 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -43,6 +43,16 @@ #define DECL_CONST(x, s) x #else /* __ASSEMBLER__ */ #define __DECL_CONST(x, s) x##s +void cga_putc(char c); + +static inline void +moo_print(const char *s) +{ + while (*s != '\0') { + cga_putc(*s); + s++; + } +} #define DECL_CONST(x, s) __DECL_CONST(x, s) #endif /* __ASSEMBLER__ */ diff --git a/kern/mutex.c b/kern/mutex.c index 7899bef..87e3d64 100644 --- a/kern/mutex.c +++ b/kern/mutex.c @@ -17,9 +17,9 @@ #ifndef X15_MUTEX_PI -#include #include +#include #include #include #include @@ -69,3 +69,12 @@ mutex_unlock_slow(struct mutex *mutex) } #endif /* X15_MUTEX_PI */ + +static int __init +mutex_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(mutex_setup, + INIT_OP_DEP(thread_setup_booter, true)); diff --git a/kern/mutex.h b/kern/mutex.h index d09a7bb..103af02 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -25,6 +25,7 @@ #ifndef _KERN_MUTEX_H #define _KERN_MUTEX_H +#include #include #ifdef X15_MUTEX_PI @@ -158,4 +159,12 @@ mutex_unlock(struct mutex *mutex) #endif /* X15_MUTEX_PI */ +/* + * This init operation provides : + * - uncontended mutex locking + * + * Contended locking may only occur after starting the scheduler. + */ +INIT_OP_DECLARE(mutex_setup); + #endif /* _KERN_MUTEX_H */ diff --git a/kern/panic.c b/kern/panic.c index 9e7d1d5..c610e3f 100644 --- a/kern/panic.c +++ b/kern/panic.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -54,3 +55,14 @@ panic(const char *format, ...) * Never reached. */ } + +static int __init +panic_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(panic_setup, + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(strace_setup, true)); diff --git a/kern/panic.h b/kern/panic.h index e098057..e6cbe8b 100644 --- a/kern/panic.h +++ b/kern/panic.h @@ -20,10 +20,18 @@ #include +#include + /* * Print the given message and halt the system immediately. */ noreturn void panic(const char *format, ...) __attribute__((format(printf, 1, 2))); +/* + * This init operation provides : + * - module fully initialized + */ +INIT_OP_DECLARE(panic_setup); + #endif /* _KERN_PANIC_H */ diff --git a/kern/percpu.c b/kern/percpu.c index c21376f..7621bb2 100644 --- a/kern/percpu.c +++ b/kern/percpu.c @@ -36,13 +36,16 @@ static void *percpu_area_content __initdata; static size_t percpu_area_size __initdata; static int percpu_skip_warning __initdata; -void __init +static int __init percpu_bootstrap(void) { percpu_areas[0] = &_percpu; + return 0; } -void __init +INIT_OP_DEFINE(percpu_bootstrap); + +static int __init percpu_setup(void) { struct vm_page *page; @@ -54,7 +57,7 @@ percpu_setup(void) assert(vm_page_aligned(percpu_area_size)); if (percpu_area_size == 0) { - return; + return 0; } order = vm_page_order(percpu_area_size); @@ -66,8 +69,13 @@ percpu_setup(void) percpu_area_content = vm_page_direct_ptr(page); memcpy(percpu_area_content, &_percpu, percpu_area_size); + return 0; } +INIT_OP_DEFINE(percpu_setup, + INIT_OP_DEP(percpu_bootstrap, true), + INIT_OP_DEP(vm_page_setup, true)); + int __init percpu_add(unsigned int cpu) { @@ -108,7 +116,7 @@ out: return 0; } -void +static int __init percpu_cleanup(void) { struct vm_page *page; @@ -117,4 +125,10 @@ percpu_cleanup(void) va = (uintptr_t)percpu_area_content; page = vm_page_lookup(vm_page_direct_pa(va)); vm_page_free(page, vm_page_order(percpu_area_size)); + return 0; } + +INIT_OP_DEFINE(percpu_cleanup, + INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(vm_page_setup, true)); diff --git a/kern/percpu.h b/kern/percpu.h index 18eb8aa..879767f 100644 --- a/kern/percpu.h +++ b/kern/percpu.h @@ -57,6 +57,7 @@ #include #include +#include #include #define PERCPU_SECTION .percpu @@ -94,25 +95,6 @@ percpu_area(unsigned int cpu) return area; } -/* - * Early initialization of the percpu module. - * - * This function registers the percpu section as the percpu area of the - * BSP. If a percpu variable is modified before calling percpu_setup(), - * the modification will be part of the percpu section and propagated to - * new percpu areas. - */ -void percpu_bootstrap(void); - -/* - * Complete initialization of the percpu module. - * - * The BSP keeps using the percpu section, but its content is copied to a - * dedicated block of memory used as a template for subsequently added - * processors. - */ -void percpu_setup(void); - /* * Register a processor. * @@ -123,8 +105,16 @@ void percpu_setup(void); int percpu_add(unsigned int cpu); /* - * Release init data allocated for setup. + * This init operation provides : + * - percpu section is registered as the BSP percpu area + */ +INIT_OP_DECLARE(percpu_bootstrap); + +/* + * This init operation provides : + * - percpu section is copied to a kernel buffer subsequently used as + * the template for future percpu areas */ -void percpu_cleanup(void); +INIT_OP_DECLARE(percpu_setup); #endif /* _KERN_PERCPU_H */ diff --git a/kern/printf.c b/kern/printf.c index 64324a0..68cf4ba 100644 --- a/kern/printf.c +++ b/kern/printf.c @@ -17,7 +17,9 @@ #include #include +#include #include +#include #include /* @@ -61,8 +63,14 @@ vprintf(const char *format, va_list ap) return length; } -void +static int __init printf_setup(void) { spinlock_init(&printf_lock); + return 0; } + +INIT_OP_DEFINE(printf_setup, + INIT_OP_DEP(boot_bootstrap_console, true), + INIT_OP_DEP(console_bootstrap, true), + INIT_OP_DEP(spinlock_setup, true)); diff --git a/kern/printf.h b/kern/printf.h index 0aed720..8185c73 100644 --- a/kern/printf.h +++ b/kern/printf.h @@ -33,12 +33,18 @@ #include +#include + int printf(const char *format, ...) __attribute__((format(printf, 1, 2))); int vprintf(const char *format, va_list ap) __attribute__((format(printf, 1, 0))); -void printf_setup(void); +/* + * This init operation provides : + * - printf is usable + */ +INIT_OP_DECLARE(printf_setup); #endif /* _KERN_PRINTF_H */ diff --git a/kern/rdxtree.c b/kern/rdxtree.c index 77f6a68..77005b6 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -895,10 +896,14 @@ rdxtree_remove_all(struct rdxtree *tree) } } -void +static int __init rdxtree_setup(void) { kmem_cache_init(&rdxtree_node_cache, "rdxtree_node", sizeof(struct rdxtree_node), 0, rdxtree_node_ctor, KMEM_CACHE_PAGE_ONLY); + return 0; } + +INIT_OP_DEFINE(rdxtree_setup, + INIT_OP_DEP(kmem_bootstrap, true)); diff --git a/kern/rdxtree.h b/kern/rdxtree.h index a30512c..6ae1e6e 100644 --- a/kern/rdxtree.h +++ b/kern/rdxtree.h @@ -29,6 +29,7 @@ #include #include +#include #include typedef uint64_t rdxtree_key_t; @@ -200,8 +201,9 @@ rdxtree_iter_key(const struct rdxtree_iter *iter) void rdxtree_remove_all(struct rdxtree *tree); /* - * Initialize the rdxtree module. + * This init operation provides : + * - module fully initialized */ -void rdxtree_setup(void); +INIT_OP_DECLARE(rdxtree_setup); #endif /* _KERN_RDXTREE_H */ diff --git a/kern/shell.c b/kern/shell.c index 6be17cd..a27c53f 100644 --- a/kern/shell.c +++ b/kern/shell.c @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -1183,7 +1182,7 @@ shell_run(void *arg) } } -void __init +static int __init shell_setup(void) { unsigned long i; @@ -1195,8 +1194,15 @@ shell_setup(void) error = shell_cmd_register(&shell_default_cmds[i]); error_check(error, "shell_cmd_register"); } + + return 0; } +INIT_OP_DEFINE(shell_setup, + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(printf_setup, true)); + void __init shell_start(void) { diff --git a/kern/shell.h b/kern/shell.h index 81be314..cf56ceb 100644 --- a/kern/shell.h +++ b/kern/shell.h @@ -21,6 +21,7 @@ #ifndef _KERN_SHELL_H #define _KERN_SHELL_H +#include #include #include @@ -64,13 +65,6 @@ void shell_cmd_init(struct shell_cmd *cmd, const char *name, shell_fn_t fn, const char *usage, const char *short_desc, const char *long_desc); -/* - * Initialize the shell module. - * - * On return, shell commands can be registered. - */ -void shell_setup(void); - /* * Start the shell thread. */ @@ -93,4 +87,11 @@ int shell_cmd_register(struct shell_cmd *cmd); #define shell_start() #endif /* X15_SHELL */ +/* + * This init operation provides : + * - commands can be registered + * - module fully initialized + */ +INIT_OP_DECLARE(shell_setup); + #endif /* _KERN_SHELL_H */ diff --git a/kern/shutdown.c b/kern/shutdown.c index 11a5183..1c95041 100644 --- a/kern/shutdown.c +++ b/kern/shutdown.c @@ -21,6 +21,7 @@ #include #include #include +#include #include static struct plist shutdown_ops_list; @@ -54,20 +55,40 @@ static struct shell_cmd shutdown_shell_cmds[] = { "reboot the system"), }; +static int __init +shutdown_setup_shell(void) +{ + SHELL_REGISTER_CMDS(shutdown_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(shutdown_setup_shell, + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(shutdown_setup, true)); + #endif /* X15_SHELL */ -void __init -shutdown_setup(void) +static int __init +shutdown_bootstrap(void) { plist_init(&shutdown_ops_list); + return 0; } -void __init -shutdown_register_shell_cmds(void) +INIT_OP_DEFINE(shutdown_bootstrap); + +static int __init +shutdown_setup(void) { - SHELL_REGISTER_CMDS(shutdown_shell_cmds); + return 0; } +INIT_OP_DEFINE(shutdown_setup, + INIT_OP_DEP(boot_setup_shutdown, true), + INIT_OP_DEP(cpu_setup, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shutdown_bootstrap, true)); + void __init shutdown_register(struct shutdown_ops *ops, unsigned int priority) { diff --git a/kern/shutdown.h b/kern/shutdown.h index f7bc187..f61079b 100644 --- a/kern/shutdown.h +++ b/kern/shutdown.h @@ -20,6 +20,7 @@ #include +#include #include struct shutdown_ops { @@ -27,13 +28,22 @@ struct shutdown_ops { void (*reset)(void); }; -void shutdown_setup(void); - -void shutdown_register_shell_cmds(void); - void shutdown_register(struct shutdown_ops *ops, unsigned int priority); noreturn void shutdown_halt(void); noreturn void shutdown_reboot(void); +/* + * This init operation provides : + * - registration of shutdown operations + */ +INIT_OP_DECLARE(shutdown_bootstrap); + +/* + * This init operation provides : + * - all shutdown operations have been registered + * - module fully initialized + */ +INIT_OP_DECLARE(shutdown_setup); + #endif /* _KERN_SHUTDOWN_H */ diff --git a/kern/sleepq.c b/kern/sleepq.c index d8bbbdd..cc85705 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -193,7 +193,7 @@ sleepq_ctor(void *ptr) sleepq->next_free = NULL; } -void __init +static int __init sleepq_bootstrap(void) { unsigned int i; @@ -205,15 +205,24 @@ sleepq_bootstrap(void) for (i = 0; i < ARRAY_SIZE(sleepq_cond_htable); i++) { sleepq_bucket_init(&sleepq_cond_htable[i]); } + + return 0; } -void __init +INIT_OP_DEFINE(sleepq_bootstrap); + +static int __init sleepq_setup(void) { kmem_cache_init(&sleepq_cache, "sleepq", sizeof(struct sleepq), CPU_L1_SIZE, sleepq_ctor, 0); + return 0; } +INIT_OP_DEFINE(sleepq_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(sleepq_bootstrap, true)); + struct sleepq * sleepq_create(void) { diff --git a/kern/sleepq.h b/kern/sleepq.h index e1f7863..0660226 100644 --- a/kern/sleepq.h +++ b/kern/sleepq.h @@ -40,20 +40,6 @@ struct sleepq; -/* - * Early initialization of the sleepq module. - * - * This module is initialized by architecture-specific code. It should - * be one of the first modules to be initialized since it's used by - * synchronization objects that may be accessed very early. - */ -void sleepq_bootstrap(void); - -/* - * Initialize the sleepq module. - */ -void sleepq_setup(void); - /* * Create/destroy a sleep queue. */ @@ -136,4 +122,17 @@ void sleepq_wait(struct sleepq *sleepq, const char *wchan); */ void sleepq_signal(struct sleepq *sleepq); +/* + * This init operation provides : + * - ? TODO Review + */ +INIT_OP_DECLARE(sleepq_bootstrap); + +/* + * This init operation provides : + * - sleepq creation + * - module fully initialized + */ +INIT_OP_DECLARE(sleepq_setup); + #endif /* _KERN_SLEEPQ_H */ diff --git a/kern/spinlock.c b/kern/spinlock.c index 6b23b36..fcb7c7b 100644 --- a/kern/spinlock.c +++ b/kern/spinlock.c @@ -69,6 +69,7 @@ #include #include +#include #include #include #include @@ -329,3 +330,12 @@ spinlock_unlock_slow(struct spinlock *lock) next_qnode = spinlock_get_remote_qnode(next_qid); atomic_store_release(&next_qnode->locked, false); } + +static int __init +spinlock_setup(void) +{ + return 0; +} + +INIT_OP_DEFINE(spinlock_setup, + INIT_OP_DEP(thread_setup_booter, true)); diff --git a/kern/spinlock.h b/kern/spinlock.h index 4dc4c83..d4105da 100644 --- a/kern/spinlock.h +++ b/kern/spinlock.h @@ -26,6 +26,7 @@ #ifndef _KERN_SPINLOCK_H #define _KERN_SPINLOCK_H +#include #include #include #include @@ -162,4 +163,12 @@ spinlock_unlock_intr_restore(struct spinlock *lock, unsigned long flags) thread_preempt_enable(); } +/* + * This init operation provides : + * - uncontended spinlock locking + * + * Contended locking may only occur after starting APs. + */ +INIT_OP_DECLARE(spinlock_setup); + #endif /* _KERN_SPINLOCK_H */ diff --git a/kern/sref.c b/kern/sref.c index 7167eff..9c49a42 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -816,7 +816,7 @@ sref_manage(void *arg) /* Never reached */ } -void __init +static int __init sref_bootstrap(void) { spinlock_init(&sref_data.lock); @@ -828,8 +828,13 @@ sref_bootstrap(void) syscnt_register(&sref_data.sc_true_zeroes, "sref_true_zeroes"); sref_cache_init(sref_cache_get(), 0); + + return 0; } +INIT_OP_DEFINE(sref_bootstrap, + INIT_OP_DEP(syscnt_setup, true)); + static void __init sref_setup_manager(struct sref_cache *cache, unsigned int cpu) { @@ -861,7 +866,7 @@ sref_setup_manager(struct sref_cache *cache, unsigned int cpu) cache->manager = manager; } -void __init +static int __init sref_setup(void) { unsigned int i; @@ -873,8 +878,20 @@ sref_setup(void) for (i = 0; i < cpu_count(); i++) { sref_setup_manager(percpu_ptr(sref_cache, i), i); } + + return 0; } +INIT_OP_DEFINE(sref_setup, + INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(sref_bootstrap, true), + INIT_OP_DEP(syscnt_setup, true), + INIT_OP_DEP(thread_setup, true)); + void sref_register(void) { diff --git a/kern/sref.h b/kern/sref.h index ac16b13..5299b9d 100644 --- a/kern/sref.h +++ b/kern/sref.h @@ -48,20 +48,6 @@ typedef void (*sref_noref_fn_t)(struct sref_counter *); #include -/* - * Early initialization of the sref module. - * - * This function depends on the availability of percpu variables. - */ -void sref_bootstrap(void); - -/* - * Initialize the sref module. - * - * This function mostly takes care of setting up periodic maintenance. - */ -void sref_setup(void); - /* * Manage registration of the current processor. * diff --git a/kern/syscnt.c b/kern/syscnt.c index d5fd28a..7cceaba 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -32,13 +32,6 @@ static struct list syscnt_list; static struct mutex syscnt_lock; -void __init -syscnt_setup(void) -{ - list_init(&syscnt_list); - mutex_init(&syscnt_lock); -} - #ifdef X15_SHELL static void @@ -50,21 +43,36 @@ syscnt_shell_info(int argc, char **argv) syscnt_info(prefix); } - static struct shell_cmd syscnt_shell_cmds[] = { SHELL_CMD_INITIALIZER("syscnt_info", syscnt_shell_info, "syscnt_info []", "display information about system counters"), }; +static int __init +syscnt_setup_shell(void) +{ + SHELL_REGISTER_CMDS(syscnt_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(syscnt_setup_shell, + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(syscnt_setup, true)); + #endif /* X15_SHELL */ -void __init -syscnt_register_shell_cmds(void) +static int __init +syscnt_setup(void) { - SHELL_REGISTER_CMDS(syscnt_shell_cmds); + list_init(&syscnt_list); + mutex_init(&syscnt_lock); + return 0; } +INIT_OP_DEFINE(syscnt_setup, + INIT_OP_DEP(mutex_setup, true)); + void __init syscnt_register(struct syscnt *syscnt, const char *name) { diff --git a/kern/syscnt.h b/kern/syscnt.h index 35a291d..816aa4b 100644 --- a/kern/syscnt.h +++ b/kern/syscnt.h @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -42,14 +43,6 @@ */ struct syscnt; -/* - * Initialize the syscnt module. - * - * This module is initialized by architecture-specific code. It is normally - * safe to call this function very early at boot time. - */ -void syscnt_setup(void); - /* * Initialize and register the given counter. * @@ -57,11 +50,6 @@ void syscnt_setup(void); */ void syscnt_register(struct syscnt *syscnt, const char *name); -/* - * Register shell commands. - */ -void syscnt_register_shell_cmds(void); - #ifdef ATOMIC_HAVE_64B_OPS static inline void @@ -123,4 +111,10 @@ syscnt_dec(struct syscnt *syscnt) */ void syscnt_info(const char *prefix); +/* + * This init operation provides : + * - registration of system counters + */ +INIT_OP_DECLARE(syscnt_setup); + #endif /* _KERN_SYSCNT_H */ diff --git a/kern/task.c b/kern/task.c index 9764cfe..ab82ad8 100644 --- a/kern/task.c +++ b/kern/task.c @@ -99,9 +99,22 @@ static struct shell_cmd task_shell_cmds[] = { "display tasks and threads"), }; +static int __init +task_setup_shell(void) +{ + SHELL_REGISTER_CMDS(task_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(task_setup_shell, + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(task_setup, true), + INIT_OP_DEP(thread_setup, true)); + #endif /* X15_SHELL */ -void __init +static int __init task_setup(void) { kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0); @@ -109,10 +122,14 @@ task_setup(void) spinlock_init(&task_list_lock); task_init(kernel_task, "x15", kernel_map); list_insert_head(&task_list, &kernel_task->node); - - SHELL_REGISTER_CMDS(task_shell_cmds); + return 0; } +INIT_OP_DEFINE(task_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(spinlock_setup, true), + INIT_OP_DEP(vm_map_setup, true)); + int task_create(struct task **taskp, const char *name) { diff --git a/kern/task.h b/kern/task.h index 591e8d7..1711fbe 100644 --- a/kern/task.h +++ b/kern/task.h @@ -19,6 +19,7 @@ #define _KERN_TASK_H #include +#include #include #include #include @@ -74,11 +75,6 @@ task_get_vm_map(const struct task *task) return task->map; } -/* - * Initialize the task module. - */ -void task_setup(void); - /* * Create a task. */ @@ -119,4 +115,11 @@ struct thread * task_lookup_thread(struct task *task, const char *name); */ void task_info(struct task *task); +/* + * This init operation provides : + * - task creation + * - module fully initialized + */ +INIT_OP_DECLARE(task_setup); + #endif /* _KERN_TASK_H */ diff --git a/kern/thread.c b/kern/thread.c index a48a08a..976b464 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1687,12 +1687,10 @@ thread_reset_real_priority(struct thread *thread) } static void __init -thread_bootstrap_common(unsigned int cpu) +thread_init_booter(unsigned int cpu) { struct thread *booter; - cpumap_set(&thread_active_runqs, cpu); - /* Initialize only what's needed during bootstrap */ booter = &thread_booters[cpu]; booter->nr_refs = 0; /* Make sure booters aren't destroyed */ @@ -1708,10 +1706,20 @@ thread_bootstrap_common(unsigned int cpu) booter->task = kernel_task; snprintf(booter->name, sizeof(booter->name), THREAD_KERNEL_PREFIX "thread_boot/%u", cpu); - thread_runq_init(percpu_ptr(thread_runq, cpu), cpu, booter); } -void __init +static int __init +thread_setup_booter(void) +{ + tcb_set_current(&thread_booters[0].tcb); + thread_init_booter(0); + return 0; +} + +INIT_OP_DEFINE(thread_setup_booter, + INIT_OP_DEP(tcb_setup, true)); + +static int __init thread_bootstrap(void) { cpumap_zero(&thread_active_runqs); @@ -1719,15 +1727,14 @@ thread_bootstrap(void) thread_fs_highest_round = THREAD_FS_INITIAL_ROUND; - tcb_set_current(&thread_booters[0].tcb); - thread_bootstrap_common(0); + cpumap_set(&thread_active_runqs, 0); + thread_runq_init(cpu_local_ptr(thread_runq), 0, &thread_booters[0]); + return 0; } -void __init -thread_ap_bootstrap(void) -{ - tcb_set_current(&thread_booters[cpu_id()].tcb); -} +INIT_OP_DEFINE(thread_bootstrap, + INIT_OP_DEP(syscnt_setup, true), + INIT_OP_DEP(thread_setup_booter, true)); void thread_main(void (*fn)(void *), void *arg) @@ -2292,15 +2299,37 @@ static struct shell_cmd thread_shell_cmds[] = { "display the stack trace of a given thread"), }; +static int __init +thread_setup_shell(void) +{ + SHELL_REGISTER_CMDS(thread_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(thread_setup_shell, + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(task_setup, true), + INIT_OP_DEP(thread_setup, true)); + #endif /* X15_SHELL */ -void __init +static void __init +thread_setup_common(unsigned int cpu) +{ + assert(cpu != 0); + cpumap_set(&thread_active_runqs, cpu); + thread_init_booter(cpu); + thread_runq_init(percpu_ptr(thread_runq, cpu), cpu, &thread_booters[cpu]); +} + +static int __init thread_setup(void) { int cpu; for (cpu = 1; (unsigned int)cpu < cpu_count(); cpu++) { - thread_bootstrap_common(cpu); + thread_setup_common(cpu); } kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), @@ -2316,7 +2345,28 @@ thread_setup(void) thread_setup_runq(percpu_ptr(thread_runq, cpu)); } - SHELL_REGISTER_CMDS(thread_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(thread_setup, + INIT_OP_DEP(cpumap_setup, true), + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(pmap_setup, true), + INIT_OP_DEP(sleepq_setup, true), + INIT_OP_DEP(task_setup, true), + INIT_OP_DEP(thread_bootstrap, true), + INIT_OP_DEP(turnstile_setup, true), +#ifdef X15_THREAD_STACK_GUARD + INIT_OP_DEP(vm_kmem_setup, true), + INIT_OP_DEP(vm_map_setup, true), + INIT_OP_DEP(vm_page_setup, true), +#endif + ); + +void __init +thread_ap_setup(void) +{ + tcb_set_current(&thread_booters[cpu_id()].tcb); } int diff --git a/kern/thread.h b/kern/thread.h index 760bb5e..f46f90c 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -169,15 +169,6 @@ thread_attr_set_priority(struct thread_attr *attr, unsigned short priority) attr->priority = priority; } -/* - * Early initialization of the thread module. - * - * These function make it possible to use migration and preemption control - * operations (and in turn, spin locks) during bootstrap. - */ -void thread_bootstrap(void); -void thread_ap_bootstrap(void); - /* * Thread entry point. * @@ -186,9 +177,9 @@ void thread_ap_bootstrap(void); void thread_main(void (*fn)(void *), void *arg); /* - * Initialize the thread module. + * Initialization of the thread module on APs. */ -void thread_setup(void); +void thread_ap_setup(void); /* * Create a thread. @@ -740,4 +731,24 @@ thread_get_specific(unsigned int key) return thread_tsd_get(thread_self(), key); } +/* + * This init operation provides : + * - a dummy thread context for the BSP, allowing the use of thread_self() + */ +INIT_OP_DECLARE(thread_setup_booter); + +/* + * This init operation provides : + * - same as thread_setup_booter + * - BSP run queue initialization + */ +INIT_OP_DECLARE(thread_bootstrap); + +/* + * This init operation provides : + * - thread creation + * - module fully initialized + */ +INIT_OP_DECLARE(thread_setup); + #endif /* _KERN_THREAD_H */ diff --git a/kern/turnstile.c b/kern/turnstile.c index 24da2cd..e724c1e 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -500,7 +500,7 @@ turnstile_ctor(void *ptr) turnstile->owner = NULL; } -void __init +static int __init turnstile_bootstrap(void) { unsigned int i; @@ -508,15 +508,24 @@ turnstile_bootstrap(void) for (i = 0; i < ARRAY_SIZE(turnstile_htable); i++) { turnstile_bucket_init(&turnstile_htable[i]); } + + return 0; } -void __init +INIT_OP_DEFINE(turnstile_bootstrap); + +static int __init turnstile_setup(void) { kmem_cache_init(&turnstile_cache, "turnstile", sizeof(struct turnstile), CPU_L1_SIZE, turnstile_ctor, 0); + return 0; } +INIT_OP_DEFINE(turnstile_setup, + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(turnstile_bootstrap, true)); + struct turnstile * turnstile_create(void) { diff --git a/kern/turnstile.h b/kern/turnstile.h index 4bc8f74..74512fa 100644 --- a/kern/turnstile.h +++ b/kern/turnstile.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -100,20 +101,6 @@ turnstile_td_get_turnstile(const struct turnstile_td *td) */ void turnstile_td_propagate_priority(struct turnstile_td *td); -/* - * Early initialization of the turnstile module. - * - * This module is initialized by architecture-specific code. It should - * be one of the first modules to be initialized since it's used by - * synchronization objects that may be accessed very early. - */ -void turnstile_bootstrap(void); - -/* - * Initialize the turnstile module. - */ -void turnstile_setup(void); - /* * Create/destroy a turnstile. */ @@ -197,4 +184,17 @@ void turnstile_signal(struct turnstile *turnstile); void turnstile_own(struct turnstile *turnstile); void turnstile_disown(struct turnstile *turnstile); +/* + * This init operation provides : + * - ? TODO Review + */ +INIT_OP_DECLARE(turnstile_bootstrap); + +/* + * This init operation provides : + * - turnstile creation + * - module fully initialized + */ +INIT_OP_DECLARE(turnstile_setup); + #endif /* _KERN_TURNSTILE_H */ diff --git a/kern/work.c b/kern/work.c index 5021d6f..9aa3f40 100644 --- a/kern/work.c +++ b/kern/work.c @@ -472,7 +472,7 @@ work_thread_destroy(struct work_thread *worker) kmem_cache_free(&work_thread_cache, worker); } -void __init +static int __init work_setup(void) { unsigned int i; @@ -493,8 +493,20 @@ work_setup(void) log_info("work: threads per pool (per-cpu/global): %u/%u, spare: %u", percpu_var(work_pool_cpu_main.max_threads, 0), work_pool_main.max_threads, WORK_THREADS_SPARE); + + return 0; } +INIT_OP_DEFINE(work_setup, + INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(kmem_setup, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(panic_setup, true), + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(spinlock_setup, true), + INIT_OP_DEP(syscnt_setup, true), + INIT_OP_DEP(thread_bootstrap, true)); + void work_schedule(struct work *work, int flags) { diff --git a/kern/work.h b/kern/work.h index 6e3876f..a8df1f7 100644 --- a/kern/work.h +++ b/kern/work.h @@ -26,6 +26,8 @@ #ifndef _KERN_WORK_H #define _KERN_WORK_H +#include + /* * Work scheduling flags. */ @@ -133,11 +135,6 @@ work_init(struct work *work, work_fn_t fn) work->fn = fn; } -/* - * Initialize the work module. - */ -void work_setup(void); - /* * Schedule work for deferred processing. * @@ -156,4 +153,11 @@ void work_queue_schedule(struct work_queue *queue, int flags); */ void work_report_periodic_event(void); +/* + * This init operation provides : + * - works can be scheduled + * - module fully initialized + */ +INIT_OP_DECLARE(work_setup); + #endif /* _KERN_WORK_H */ diff --git a/kern/xcall.c b/kern/xcall.c index fffe2bc..cccb937 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -104,7 +105,7 @@ xcall_cpu_data_clear_recv_call(struct xcall_cpu_data *cpu_data) xcall_cpu_data_set_recv_call(cpu_data, NULL); } -void +static int __init xcall_setup(void) { unsigned int i; @@ -112,8 +113,15 @@ xcall_setup(void) for (i = 0; i < cpu_count(); i++) { xcall_cpu_data_init(percpu_ptr(xcall_cpu_data, i)); } + + return 0; } +INIT_OP_DEFINE(xcall_setup, + INIT_OP_DEP(percpu_setup, true), + INIT_OP_DEP(thread_bootstrap, true), + INIT_OP_DEP(spinlock_setup, true)); + void xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu) { diff --git a/kern/xcall.h b/kern/xcall.h index 37c8586..27b6af2 100644 --- a/kern/xcall.h +++ b/kern/xcall.h @@ -29,11 +29,6 @@ */ typedef void (*xcall_fn_t)(void *arg); -/* - * Initialize the xcall module. - */ -void xcall_setup(void); - /* * Run the given cross-call function on a specific processor. * diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index da372b0..dc204f8 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -49,15 +48,22 @@ vm_kmem_offset(uintptr_t va) return va - PMAP_MIN_KMEM_ADDRESS; } -void __init +static int __init vm_kmem_setup(void) { uint64_t size; size = vm_kmem_offset(PMAP_MAX_KMEM_ADDRESS); vm_object_init(&vm_kmem_kernel_object, size); + return 0; } +INIT_OP_DEFINE(vm_kmem_setup, + INIT_OP_DEP(pmap_bootstrap, true), + INIT_OP_DEP(vm_map_bootstrap, true), + INIT_OP_DEP(vm_object_setup, true), + INIT_OP_DEP(vm_page_setup, true)); + static int vm_kmem_alloc_check(size_t size) { diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index d845c1d..e10c220 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -20,6 +20,7 @@ #include +#include #include #include @@ -44,11 +45,6 @@ extern char _end; */ extern struct vm_map *kernel_map; -/* - * Initialize the vm_kmem module. - */ -void vm_kmem_setup(void); - /* * Allocate pure virtual kernel pages. * @@ -94,4 +90,10 @@ void * vm_kmem_map_pa(phys_addr_t pa, size_t size, */ void vm_kmem_unmap_pa(uintptr_t map_va, size_t map_size); +/* + * This init operation provides : + * - kernel virtual memory allocation + */ +INIT_OP_DECLARE(vm_kmem_setup); + #endif /* _VM_VM_KMEM_H */ diff --git a/vm/vm_map.c b/vm/vm_map.c index 389678b..01e0ed5 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -728,21 +728,50 @@ static struct shell_cmd vm_map_shell_cmds[] = { "display information about a VM map"), }; +static int __init +vm_map_setup_shell(void) +{ + SHELL_REGISTER_CMDS(vm_map_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(vm_map_setup_shell, + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(task_setup, true), + INIT_OP_DEP(vm_map_setup, true)); + #endif /* X15_SHELL */ -void __init -vm_map_setup(void) +static int __init +vm_map_bootstrap(void) { vm_map_init(kernel_map, kernel_pmap, PMAP_MIN_KMEM_ADDRESS, PMAP_MAX_KMEM_ADDRESS); kmem_cache_init(&vm_map_entry_cache, "vm_map_entry", sizeof(struct vm_map_entry), 0, NULL, KMEM_CACHE_PAGE_ONLY); + return 0; +} + +INIT_OP_DEFINE(vm_map_bootstrap, + INIT_OP_DEP(kmem_bootstrap, true), + INIT_OP_DEP(thread_bootstrap, true)); + +static int __init +vm_map_setup(void) +{ kmem_cache_init(&vm_map_cache, "vm_map", sizeof(struct vm_map), - 0, NULL, 0); - SHELL_REGISTER_CMDS(vm_map_shell_cmds); + 0, NULL, KMEM_CACHE_PAGE_ONLY); + return 0; } +INIT_OP_DEFINE(vm_map_setup, + INIT_OP_DEP(pmap_setup, true), + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(vm_map_bootstrap, true)); + int vm_map_create(struct vm_map **mapp) { diff --git a/vm/vm_map.h b/vm/vm_map.h index 1e17bbb..653eabd 100644 --- a/vm/vm_map.h +++ b/vm/vm_map.h @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -103,11 +104,6 @@ int vm_map_enter(struct vm_map *map, uintptr_t *startp, */ void vm_map_remove(struct vm_map *map, uintptr_t start, uintptr_t end); -/* - * Set up the vm_map module. - */ -void vm_map_setup(void); - /* * Create a VM map. */ @@ -118,4 +114,17 @@ int vm_map_create(struct vm_map **mapp); */ void vm_map_info(struct vm_map *map); +/* + * This init operation provides : + * - kernel mapping operations + */ +INIT_OP_DECLARE(vm_map_bootstrap); + +/* + * This init operation provides : + * - VM map creation + * - module fully initialized + */ +INIT_OP_DECLARE(vm_map_setup); + #endif /* _VM_VM_MAP_H */ diff --git a/vm/vm_object.c b/vm/vm_object.c index 874b26f..649d857 100644 --- a/vm/vm_object.c +++ b/vm/vm_object.c @@ -31,11 +31,17 @@ #include #include -void __init +static int __init vm_object_setup(void) { + return 0; } +INIT_OP_DEFINE(vm_object_setup, + INIT_OP_DEP(mutex_setup, true), + INIT_OP_DEP(rdxtree_setup, true), + INIT_OP_DEP(vm_page_setup, true)); + void __init vm_object_init(struct vm_object *object, uint64_t size) { diff --git a/vm/vm_object.h b/vm/vm_object.h index 046937f..9ffe711 100644 --- a/vm/vm_object.h +++ b/vm/vm_object.h @@ -26,17 +26,13 @@ #include +#include #include #include #include struct vm_object; -/* - * Initialize the vm_object module. - */ -void vm_object_setup(void); - /* * Initialize a VM object. */ @@ -75,4 +71,10 @@ void vm_object_remove(struct vm_object *object, uint64_t start, uint64_t end); */ struct vm_page * vm_object_lookup(struct vm_object *object, uint64_t offset); +/* + * This init operation provides : + * - module fully initialized + */ +INIT_OP_DECLARE(vm_object_setup); + #endif /* _VM_OBJECT_H */ diff --git a/vm/vm_page.c b/vm/vm_page.c index 950d04a..7b0eb3e 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -678,9 +679,21 @@ static struct shell_cmd vm_page_shell_cmds[] = { "display information about physical memory"), }; +static int __init +vm_page_setup_shell(void) +{ + SHELL_REGISTER_CMDS(vm_page_shell_cmds); + return 0; +} + +INIT_OP_DEFINE(vm_page_setup_shell, + INIT_OP_DEP(printf_setup, true), + INIT_OP_DEP(shell_setup, true), + INIT_OP_DEP(vm_page_setup, true)); + #endif /* X15_SHELL */ -void __init +static int __init vm_page_setup(void) { struct vm_page_boot_zone *boot_zone; @@ -741,9 +754,14 @@ vm_page_setup(void) vm_page_is_ready = 1; - SHELL_REGISTER_CMDS(vm_page_shell_cmds); + return 0; } +INIT_OP_DEFINE(vm_page_setup, + INIT_OP_DEP(boot_load_vm_page_zones, true), + INIT_OP_DEP(log_setup, true), + INIT_OP_DEP(printf_setup, true)); + /* TODO Rename to avoid confusion with "managed pages" */ void __init vm_page_manage(struct vm_page *page) diff --git a/vm/vm_page.h b/vm/vm_page.h index ea267db..4ca3420 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -191,19 +192,6 @@ void vm_page_load_heap(unsigned int zone_index, phys_addr_t start, */ int vm_page_ready(void); -/* - * Set up the vm_page module. - * - * Architecture-specific code must have loaded zones before calling this - * function. Zones must comply with the selector-to-zone-list table, - * e.g. HIGHMEM is loaded if and only if DIRECTMAP, DMA32 and DMA are loaded, - * notwithstanding zone aliasing. - * - * Once this function returns, the vm_page module is ready, and normal - * allocation functions can be used. - */ -void vm_page_setup(void); - /* * Make the given page managed by the vm_page module. * @@ -241,7 +229,7 @@ void vm_page_free(struct vm_page *page, unsigned int order); const char * vm_page_zone_name(unsigned int zone_index); /* - * Display internal information about the module. + * Log information about physical pages. */ void vm_page_log_info(void); @@ -291,4 +279,10 @@ vm_page_tryref(struct vm_page *page) return 0; } +/* + * This init operation provides : + * - module fully initialized + */ +INIT_OP_DECLARE(vm_page_setup); + #endif /* _VM_VM_PAGE_H */ diff --git a/vm/vm_setup.c b/vm/vm_setup.c deleted file mode 100644 index 9fb9c52..0000000 --- a/vm/vm_setup.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2011-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 3 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 - -void __init -vm_setup(void) -{ - vm_page_setup(); - kmem_setup(); - rdxtree_setup(); - vm_object_setup(); - vm_map_setup(); - vm_kmem_setup(); - pmap_setup(); - percpu_setup(); -} diff --git a/vm/vm_setup.h b/vm/vm_setup.h deleted file mode 100644 index f52ddb2..0000000 --- a/vm/vm_setup.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2011 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 3 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 _VM_VM_SETUP_H -#define _VM_VM_SETUP_H - -/* - * Set up the VM system. - * - * This function also initializes the kmem (kernel memory) allocator. - */ -void vm_setup(void); - -#endif /* _VM_VM_SETUP_H */ -- cgit v1.2.3 From 6e08dddccb29ff35c0a18def56f6b6f05eb43bc2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Jul 2017 20:24:29 +0200 Subject: doc/style(9): describe local variables rules --- doc/style.9.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/style.9.txt b/doc/style.9.txt index 93683d8..8d87efb 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -492,6 +492,13 @@ error_subobj: } -------------------------------------------------------------------------------- +Local variables +~~~~~~~~~~~~~~~ + +Local variables must be declared at the beginning of their scope. An empty +line separates them from the function body. A notable exception is iterators, +which may be declared inside loop statements. + Common functions and methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From cbc33352cac34371e738d9b3b672bf252f1365e8 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Jul 2017 20:45:29 +0200 Subject: kern/thread: fix thread stack guard support Since the introduction of the kernel VM object, page referencing has become more strict, preventing the thread stack guard code to function. Work around the issue by keeping the physical pages at the stack boundaries. --- kern/thread.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 976b464..df32ab4 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1931,24 +1931,18 @@ thread_alloc_stack(void) pmap_remove(kernel_pmap, va + PAGE_SIZE + stack_size, cpumap_all()); pmap_update(kernel_pmap); - vm_page_free(first_page, 0); - vm_page_free(last_page, 0); - - return (char *)va + PAGE_SIZE; + return (void *)va + PAGE_SIZE; } static void thread_free_stack(void *stack) { size_t stack_size; - char *va; + void *va; stack_size = vm_page_round(TCB_STACK_SIZE); - va = (char *)stack - PAGE_SIZE; - - vm_kmem_free_va(va, PAGE_SIZE); - vm_kmem_free(va + PAGE_SIZE, stack_size); - vm_kmem_free_va(va + PAGE_SIZE + stack_size, PAGE_SIZE); + va = (void *)stack - PAGE_SIZE; + vm_kmem_free(va, (PAGE_SIZE * 2) + stack_size); } #else /* X15_THREAD_STACK_GUARD */ -- cgit v1.2.3 From d92b162087dc0d4791ba6d573a8d766e5f7290f2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 13 Jul 2017 20:47:52 +0200 Subject: kern/log: fix call to shell_start --- kern/log.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kern/log.c b/kern/log.c index 0ddf679..3d6ee37 100644 --- a/kern/log.c +++ b/kern/log.c @@ -293,8 +293,10 @@ log_run(void *arg) * a clean ordered output. */ if (start_shell) { + spinlock_unlock_intr_restore(&log_lock, flags); shell_start(); start_shell = false; + spinlock_lock_intr_save(&log_lock, &flags); } log_index = log_consume_ctx_index(&ctx); -- cgit v1.2.3 From 9afdf7bfc8f33e98cc175c854b511a7b90aa774b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 14 Jul 2017 11:31:06 +0200 Subject: x86/acpi: fix typo --- arch/x86/machine/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index dabc7f3..7720226 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -693,7 +693,7 @@ error: * For the sake of simplicity, it has been decided to ignore legacy * specifications such as the multiprocessor specification, and use * ACPI only. If ACPI is unavailable, consider the APIC system to - * be missing and fall back to using the legaxy XT-PIC. + * be missing and fall back to using the legacy XT-PIC. */ pic_setup(); return 0; -- cgit v1.2.3 From ff184c2554bdc4ddd5efafb4d1d2dd5a8dc2f116 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 14 Jul 2017 11:40:08 +0200 Subject: Fix inclusions of kern/init.h when declaring init operations --- kern/sleepq.h | 2 ++ kern/thread.h | 1 + 2 files changed, 3 insertions(+) diff --git a/kern/sleepq.h b/kern/sleepq.h index 0660226..4d37182 100644 --- a/kern/sleepq.h +++ b/kern/sleepq.h @@ -38,6 +38,8 @@ #include +#include + struct sleepq; /* diff --git a/kern/thread.h b/kern/thread.h index f46f90c..a1b33ac 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From adcae3076edee5ed24cb06f4328f88cfa5e8998a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 14 Jul 2017 11:46:44 +0200 Subject: doc/style(9): relax boolean coercion rules about pointers Friends can be convincing, and I do have an open mind, don't I ? --- doc/style.9.txt | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index 8d87efb..7209567 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -613,19 +613,8 @@ the macro used to declare an array again in the iteration code. Boolean Coercion ~~~~~~~~~~~~~~~~ -Boolean coercion is forbidden by default, especially on pointers. Always -explicitely compare pointers to NULL and integers to 0. For example : - -[source,c] --------------------------------------------------------------------------------- -/* Read "if ptr is not invalid and if ptr and ref are equal" */ -if ((ptr != NULL) && (memcmp(ptr, ref, sizeof(*ptr)) == 0)) { - do_something(); -} --------------------------------------------------------------------------------- - -The main exception to this rule is error codes, which developers are -encouraged to use as boolean values. Here is an example : +Boolean coercion is forbidden by default. It is only allowed when +checking error codes and pointers. For example : [source,c] -------------------------------------------------------------------------------- @@ -642,8 +631,8 @@ if (error) { } -------------------------------------------------------------------------------- -The other exception is the historical use of the *int* type, rather than -the C99 *bool* type, for boolean values. +There are historical uses of the *int* type for boolean values, but all of +them should be converted to the C99 *bool* type. Side effects and conditions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From 4278f99adcbcfbd52904c0d8809184afe091c958 Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Fri, 21 Jul 2017 00:49:39 +0200 Subject: Rework mutex implementation selection --- Makefrag.am | 10 +++- configure.ac | 7 +++ kern/atomic.h | 6 +-- kern/mutex.c | 71 -------------------------- kern/mutex.h | 98 ++++++------------------------------ kern/mutex/mutex_pi_i.h | 60 ++++++++++++++++++++++ kern/mutex/mutex_pi_types.h | 39 ++++++++++++++ kern/mutex/mutex_plain.c | 65 ++++++++++++++++++++++++ kern/mutex/mutex_plain_i.h | 112 +++++++++++++++++++++++++++++++++++++++++ kern/mutex/mutex_plain_types.h | 33 ++++++++++++ kern/mutex_i.h | 53 ------------------- kern/mutex_types.h | 24 ++------- kern/rtmutex.c | 3 +- 13 files changed, 348 insertions(+), 233 deletions(-) delete mode 100644 kern/mutex.c create mode 100644 kern/mutex/mutex_pi_i.h create mode 100644 kern/mutex/mutex_pi_types.h create mode 100644 kern/mutex/mutex_plain.c create mode 100644 kern/mutex/mutex_plain_i.h create mode 100644 kern/mutex/mutex_plain_types.h delete mode 100644 kern/mutex_i.h diff --git a/Makefrag.am b/Makefrag.am index e9d4e63..c3f8d03 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -44,10 +44,12 @@ x15_SOURCES += \ kern/llsync_i.h \ kern/log2.h \ kern/macros.h \ - kern/mutex.c \ kern/mutex.h \ - kern/mutex_i.h \ kern/mutex_types.h \ + kern/mutex/mutex_pi_i.h \ + kern/mutex/mutex_pi_types.h \ + kern/mutex/mutex_plain_i.h \ + kern/mutex/mutex_plain_types.h \ kern/panic.c \ kern/panic.h \ kern/param.h \ @@ -103,6 +105,10 @@ x15_SOURCES += \ kern/xcall.c \ kern/xcall.h +if !MUTEX_PI + x15_SOURCES += kern/mutex/mutex_plain.c +endif + x15_SOURCES += \ vm/vm_adv.h \ vm/vm_inherit.h \ diff --git a/configure.ac b/configure.ac index be21f3d..dd4607c 100644 --- a/configure.ac +++ b/configure.ac @@ -100,9 +100,16 @@ AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], [maximum number of supported processors]) AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) +AS_IF([test x"$enable_mutex_pi" = xyes], + [mutex_impl="priority inheritance"], + [mutex_impl=plain]) +AC_MSG_NOTICE([mutex implementation: $mutex_impl]) + AS_IF([test x"$enable_mutex_pi" = xyes], [AC_DEFINE_UNQUOTED([X15_MUTEX_PI], [], [Enable priority inheritance for regular mutexes])]) +AM_CONDITIONAL([MUTEX_PI], + [test x"$enable_mutex_pi" = xyes]) AS_IF([test x"$enable_thread_stack_guard" = xyes], [AC_DEFINE_UNQUOTED([X15_THREAD_STACK_GUARD], [], diff --git a/kern/atomic.h b/kern/atomic.h index 75b0701..63f0ac7 100644 --- a/kern/atomic.h +++ b/kern/atomic.h @@ -116,6 +116,9 @@ MACRO_END * Common shortcuts. */ +#define atomic_load_acquire(ptr) atomic_load(ptr, ATOMIC_ACQUIRE) +#define atomic_store_release(ptr, val) atomic_store(ptr, val, ATOMIC_RELEASE) + #define atomic_cas_acquire(ptr, oval, nval) \ atomic_cas(ptr, oval, nval, ATOMIC_ACQUIRE) @@ -129,9 +132,6 @@ MACRO_END #define atomic_swap_release(ptr, val) atomic_swap(ptr, val, ATOMIC_RELEASE) #define atomic_swap_acq_rel(ptr, val) atomic_swap(ptr, val, ATOMIC_ACQ_REL) -#define atomic_fetch_add_acq_rel(ptr, val) \ - atomic_fetch_add(ptr, val, ATOMIC_ACQ_REL) - #define atomic_fetch_sub_acq_rel(ptr, val) \ atomic_fetch_sub(ptr, val, ATOMIC_ACQ_REL) diff --git a/kern/mutex.c b/kern/mutex.c deleted file mode 100644 index 7899bef..0000000 --- a/kern/mutex.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2013-2017 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 3 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 X15_MUTEX_PI - -#include -#include - -#include -#include -#include - -void -mutex_lock_slow(struct mutex *mutex) -{ - struct sleepq *sleepq; - unsigned long flags; - unsigned int state; - - sleepq = sleepq_lend(mutex, false, &flags); - - for (;;) { - state = atomic_swap_acquire(&mutex->state, MUTEX_CONTENDED); - - if (state == MUTEX_UNLOCKED) { - break; - } - - sleepq_wait(sleepq, "mutex"); - } - - if (sleepq_empty(sleepq)) { - state = atomic_swap_acquire(&mutex->state, MUTEX_LOCKED); - assert(state == MUTEX_CONTENDED); - } - - sleepq_return(sleepq, flags); -} - -void -mutex_unlock_slow(struct mutex *mutex) -{ - struct sleepq *sleepq; - unsigned long flags; - - sleepq = sleepq_acquire(mutex, false, &flags); - - if (sleepq == NULL) { - return; - } - - sleepq_signal(sleepq); - - sleepq_release(sleepq, flags); -} - -#endif /* X15_MUTEX_PI */ diff --git a/kern/mutex.h b/kern/mutex.h index 1100b77..e599e8a 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -18,75 +18,31 @@ * Mutual exclusion sleep locks. * * Unlike spin locks, acquiring a mutex may make the calling thread sleep. - * - * TODO Adaptive spinning. */ #ifndef _KERN_MUTEX_H #define _KERN_MUTEX_H -#include - -#ifdef X15_MUTEX_PI - -#include - -struct mutex; - -#define mutex_assert_locked(mutex) rtmutex_assert_locked(&(mutex)->rtmutex) - -static inline void -mutex_init(struct mutex *mutex) -{ - rtmutex_init(&mutex->rtmutex); -} - -static inline int -mutex_trylock(struct mutex *mutex) -{ - return rtmutex_trylock(&mutex->rtmutex); -} - -static inline void -mutex_lock(struct mutex *mutex) -{ - rtmutex_lock(&mutex->rtmutex); -} - -static inline void -mutex_unlock(struct mutex *mutex) -{ - rtmutex_unlock(&mutex->rtmutex); +#if defined(X15_MUTEX_PI) +#include +#else +#include +#endif - /* - * If this mutex was used along with a condition variable, wake up - * a potential pending waiter. This must be done after the mutex is - * unlocked so that a higher priority thread can directly acquire it. - */ - thread_wakeup_last_cond(); -} - -#else /* X15_MUTEX_PI */ - -#include -#include -#include -#include +#include #include -struct mutex; - -#define mutex_assert_locked(mutex) assert((mutex)->state != MUTEX_UNLOCKED) - /* * Initialize a mutex. */ static inline void mutex_init(struct mutex *mutex) { - mutex->state = MUTEX_UNLOCKED; + mutex_impl_init(mutex); } +#define mutex_assert_locked(mutex) mutex_impl_assert_locked(mutex) + /* * Attempt to lock the given mutex. * @@ -97,37 +53,20 @@ mutex_init(struct mutex *mutex) static inline int mutex_trylock(struct mutex *mutex) { - unsigned int state; - - state = mutex_lock_fast(mutex); - - if (unlikely(state != MUTEX_UNLOCKED)) { - assert((state == MUTEX_LOCKED) || (state == MUTEX_CONTENDED)); - return ERROR_BUSY; - } - - return 0; + return mutex_impl_trylock(mutex); } /* * Lock a mutex. * - * If the mutex is already locked, the calling thread sleeps until the - * mutex is unlocked. + * On return, the mutex is locked. A mutex can only be locked once. * - * A mutex can only be locked once. + * This function may sleep. */ static inline void mutex_lock(struct mutex *mutex) { - unsigned int state; - - state = mutex_lock_fast(mutex); - - if (unlikely(state != MUTEX_UNLOCKED)) { - assert((state == MUTEX_LOCKED) || (state == MUTEX_CONTENDED)); - mutex_lock_slow(mutex); - } + mutex_impl_lock(mutex); } /* @@ -139,14 +78,7 @@ mutex_lock(struct mutex *mutex) static inline void mutex_unlock(struct mutex *mutex) { - unsigned int state; - - state = mutex_unlock_fast(mutex); - - if (unlikely(state != MUTEX_LOCKED)) { - assert(state == MUTEX_CONTENDED); - mutex_unlock_slow(mutex); - } + mutex_impl_unlock(mutex); /* * If this mutex was used along with a condition variable, wake up @@ -155,6 +87,4 @@ mutex_unlock(struct mutex *mutex) thread_wakeup_last_cond(); } -#endif /* X15_MUTEX_PI */ - #endif /* _KERN_MUTEX_H */ diff --git a/kern/mutex/mutex_pi_i.h b/kern/mutex/mutex_pi_i.h new file mode 100644 index 0000000..6c39db7 --- /dev/null +++ b/kern/mutex/mutex_pi_i.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_MUTEX_PI_I_H +#define _KERN_MUTEX_PI_I_H + +#ifndef _KERN_MUTEX_H +#error "don't include directly," \ + " use instead" +#endif + +#include +#include + +/* + * Interface exported to the public mutex header. + */ + +static inline void +mutex_impl_init(struct mutex *mutex) +{ + rtmutex_init(&mutex->rtmutex); +} + +#define mutex_impl_assert_locked(mutex) \ + rtmutex_assert_locked(&(mutex)->rtmutex) + +static inline int +mutex_impl_trylock(struct mutex *mutex) +{ + return rtmutex_trylock(&mutex->rtmutex); +} + +static inline void +mutex_impl_lock(struct mutex *mutex) +{ + rtmutex_lock(&mutex->rtmutex); +} + +static inline void +mutex_impl_unlock(struct mutex *mutex) +{ + rtmutex_unlock(&mutex->rtmutex); +} + +#endif /* _KERN_MUTEX_PI_I_H */ diff --git a/kern/mutex/mutex_pi_types.h b/kern/mutex/mutex_pi_types.h new file mode 100644 index 0000000..d9ebb6e --- /dev/null +++ b/kern/mutex/mutex_pi_types.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _KERN_MUTEX_PI_TYPES_H +#define _KERN_MUTEX_PI_TYPES_H + +#ifndef _KERN_MUTEX_TYPES_H +#error "don't include directly," \ + " use instead" +#endif + +#include + +/* + * Do not directly alias rtmutex to make sure they cannot be used + * with condition variables by mistake. + */ +struct mutex { + struct rtmutex rtmutex; +}; + +#endif /* _KERN_MUTEX_PI_TYPES_H */ diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c new file mode 100644 index 0000000..a925a5a --- /dev/null +++ b/kern/mutex/mutex_plain.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 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 3 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 + +void +mutex_plain_lock_slow(struct mutex *mutex) +{ + unsigned int state; + struct sleepq *sleepq; + unsigned long flags; + + sleepq = sleepq_lend(mutex, false, &flags); + + for (;;) { + state = atomic_swap_release(&mutex->state, MUTEX_CONTENDED); + + if (state == MUTEX_UNLOCKED) { + break; + } + + sleepq_wait(sleepq, "mutex"); + } + + if (sleepq_empty(sleepq)) { + /* TODO Review memory order */ + atomic_store(&mutex->state, MUTEX_LOCKED, ATOMIC_RELEASE); + } + + sleepq_return(sleepq, flags); +} + +void +mutex_plain_unlock_slow(struct mutex *mutex) +{ + struct sleepq *sleepq; + unsigned long flags; + + sleepq = sleepq_acquire(mutex, false, &flags); + + if (sleepq != NULL) { + sleepq_signal(sleepq); + sleepq_release(sleepq, flags); + } +} diff --git a/kern/mutex/mutex_plain_i.h b/kern/mutex/mutex_plain_i.h new file mode 100644 index 0000000..9e41ff2 --- /dev/null +++ b/kern/mutex/mutex_plain_i.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_MUTEX_PLAIN_I_H +#define _KERN_MUTEX_PLAIN_I_H + +#ifndef _KERN_MUTEX_H +#error "don't include directly," \ + " use instead" +#endif + +#include +#include +#include +#include + +#define MUTEX_UNLOCKED 0 +#define MUTEX_LOCKED 1 +#define MUTEX_CONTENDED 2 + +static inline void +mutex_plain_init(struct mutex *mutex) +{ + mutex->state = MUTEX_UNLOCKED; +} + +#define mutex_plain_assert_locked(mutex) \ + assert((mutex)->state != MUTEX_UNLOCKED) + +static inline int +mutex_plain_lock_fast(struct mutex *mutex) +{ + unsigned int state; + + state = atomic_cas_acquire(&mutex->state, MUTEX_UNLOCKED, MUTEX_LOCKED); + + if (unlikely(state != MUTEX_UNLOCKED)) { + return ERROR_BUSY; + } + + return 0; +} + +static inline int +mutex_plain_unlock_fast(struct mutex *mutex) +{ + unsigned int state; + + state = atomic_swap_release(&mutex->state, MUTEX_UNLOCKED); + + if (unlikely(state == MUTEX_CONTENDED)) { + return ERROR_BUSY; + } + + return 0; +} + +void mutex_plain_lock_slow(struct mutex *mutex); +void mutex_plain_unlock_slow(struct mutex *mutex); + +/* + * Interface exported to the public mutex header. + */ + +#define mutex_impl_init mutex_plain_init +#define mutex_impl_assert_locked mutex_plain_assert_locked + +static inline int +mutex_impl_trylock(struct mutex *mutex) +{ + return mutex_plain_lock_fast(mutex); +} + +static inline void +mutex_impl_lock(struct mutex *mutex) +{ + int error; + + error = mutex_plain_lock_fast(mutex); + + if (unlikely(error)) { + mutex_plain_lock_slow(mutex); + } +} + +static inline void +mutex_impl_unlock(struct mutex *mutex) +{ + int error; + + error = mutex_plain_unlock_fast(mutex); + + if (unlikely(error)) { + mutex_plain_unlock_slow(mutex); + } +} + +#endif /* _KERN_MUTEX_PLAIN_I_H */ diff --git a/kern/mutex/mutex_plain_types.h b/kern/mutex/mutex_plain_types.h new file mode 100644 index 0000000..02731e9 --- /dev/null +++ b/kern/mutex/mutex_plain_types.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _KERN_MUTEX_PLAIN_TYPES_H +#define _KERN_MUTEX_PLAIN_TYPES_H + +#ifndef _KERN_MUTEX_TYPES_H +#error "don't include directly," \ + " use instead" +#endif + +struct mutex { + unsigned int state; +}; + +#endif /* _KERN_MUTEX_PLAIN_TYPES_H */ diff --git a/kern/mutex_i.h b/kern/mutex_i.h deleted file mode 100644 index a4a40eb..0000000 --- a/kern/mutex_i.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2013-2017 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 3 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 _KERN_MUTEX_I_H -#define _KERN_MUTEX_I_H - -#ifndef X15_MUTEX_PI - -#include -#include -#include - -#define MUTEX_UNLOCKED 0 -#define MUTEX_LOCKED 1 -#define MUTEX_CONTENDED 2 - -static inline unsigned int -mutex_lock_fast(struct mutex *mutex) -{ - return atomic_cas_acquire(&mutex->state, MUTEX_UNLOCKED, MUTEX_LOCKED); -} - -static inline unsigned int -mutex_unlock_fast(struct mutex *mutex) -{ - unsigned int state; - - state = atomic_swap_release(&mutex->state, MUTEX_UNLOCKED); - assert((state == MUTEX_LOCKED) || (state == MUTEX_CONTENDED)); - return state; -} - -void mutex_lock_slow(struct mutex *mutex); - -void mutex_unlock_slow(struct mutex *mutex); - -#endif /* X15_MUTEX_PI */ - -#endif /* _KERN_MUTEX_I_H */ diff --git a/kern/mutex_types.h b/kern/mutex_types.h index 4b7947f..432eab3 100644 --- a/kern/mutex_types.h +++ b/kern/mutex_types.h @@ -21,24 +21,10 @@ #ifndef _KERN_MUTEX_TYPES_H #define _KERN_MUTEX_TYPES_H -#ifdef X15_MUTEX_PI - -#include - -/* - * Do not directly alias rtmutex to make sure they cannot be used - * with condition variables by mistake. - */ -struct mutex { - struct rtmutex rtmutex; -}; - -#else /* X15_MUTEX_PI */ - -struct mutex { - unsigned int state; -}; - -#endif /* X15_MUTEX_PI */ +#if defined(X15_MUTEX_PI) +#include +#else +#include +#endif #endif /* _KERN_MUTEX_TYPES_H */ diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 6f639dd..6909ce1 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -36,7 +36,7 @@ void rtmutex_lock_slow(struct rtmutex *rtmutex) { struct turnstile *turnstile; - uintptr_t owner, prev_owner; + uintptr_t owner, prev_owner; /* TODO Review names */ struct thread *thread; uintptr_t bits; @@ -64,6 +64,7 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) turnstile_own(turnstile); if (turnstile_empty(turnstile)) { + /* TODO Review memory order */ prev_owner = atomic_swap_acquire(&rtmutex->owner, owner); assert(prev_owner == (owner | bits)); } -- cgit v1.2.3 From 5c2cf8fff7a1d6dc6b88615df5433ddccbbcf51f Mon Sep 17 00:00:00 2001 From: Agustina Arzille Date: Fri, 21 Jul 2017 00:50:34 +0200 Subject: kern/mutex: new adaptive spinning mutex implementation --- Makefrag.am | 6 ++ configure.ac | 17 ++++- kern/mutex.h | 6 ++ kern/mutex/mutex_adaptive.c | 138 ++++++++++++++++++++++++++++++++++++++ kern/mutex/mutex_adaptive_i.h | 120 +++++++++++++++++++++++++++++++++ kern/mutex/mutex_adaptive_types.h | 35 ++++++++++ kern/mutex_types.h | 2 + kern/sleepq.c | 27 ++++++++ kern/sleepq.h | 6 ++ kern/thread.c | 15 ++++- kern/thread.h | 8 +++ 11 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 kern/mutex/mutex_adaptive.c create mode 100644 kern/mutex/mutex_adaptive_i.h create mode 100644 kern/mutex/mutex_adaptive_types.h diff --git a/Makefrag.am b/Makefrag.am index c3f8d03..2ed9f77 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -46,6 +46,8 @@ x15_SOURCES += \ kern/macros.h \ kern/mutex.h \ kern/mutex_types.h \ + kern/mutex/mutex_adaptive_i.h \ + kern/mutex/mutex_adaptive_types.h \ kern/mutex/mutex_pi_i.h \ kern/mutex/mutex_pi_types.h \ kern/mutex/mutex_plain_i.h \ @@ -105,9 +107,13 @@ x15_SOURCES += \ kern/xcall.c \ kern/xcall.h +if MUTEX_ADAPTIVE + x15_SOURCES += kern/mutex/mutex_adaptive.c +else if !MUTEX_PI x15_SOURCES += kern/mutex/mutex_plain.c endif +endif x15_SOURCES += \ vm/vm_adv.h \ diff --git a/configure.ac b/configure.ac index dd4607c..520fa02 100644 --- a/configure.ac +++ b/configure.ac @@ -47,6 +47,10 @@ AC_ARG_WITH([max-cpus], [opt_max_cpus=$withval], [opt_max_cpus=128]) +AC_ARG_ENABLE([mutex-adaptive], + [AS_HELP_STRING([--enable-mutex-adaptive], + [enable adaptive spinning mutexes])]) + AC_ARG_ENABLE([mutex-pi], [AS_HELP_STRING([--enable-mutex-pi], [enable priority inheritance for regular mutexes @@ -100,11 +104,22 @@ AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], [maximum number of supported processors]) AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) -AS_IF([test x"$enable_mutex_pi" = xyes], +AS_IF([test x"$enable_mutex_adaptive" = xyes -a x"$enable_mutex_pi" = xyes], + [AC_MSG_ERROR([--enable-mutex-adaptive and --enable-mutex-pi are mutually exclusive])]) + +AS_IF([test x"$enable_mutex_adaptive" = xyes], + [mutex_impl="adaptive spinning"], + [test x"$enable_mutex_pi" = xyes], [mutex_impl="priority inheritance"], [mutex_impl=plain]) AC_MSG_NOTICE([mutex implementation: $mutex_impl]) +AS_IF([test x"$enable_mutex_adaptive" = xyes], + [AC_DEFINE_UNQUOTED([X15_MUTEX_ADAPTIVE], [], + [Enable adaptive mutexes])]) +AM_CONDITIONAL([MUTEX_ADAPTIVE], + [test x"$enable_mutex_adaptive" = xyes]) + AS_IF([test x"$enable_mutex_pi" = xyes], [AC_DEFINE_UNQUOTED([X15_MUTEX_PI], [], [Enable priority inheritance for regular mutexes])]) diff --git a/kern/mutex.h b/kern/mutex.h index e599e8a..2936d82 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -23,8 +23,14 @@ #ifndef _KERN_MUTEX_H #define _KERN_MUTEX_H +#if defined(X15_MUTEX_PI) && defined(X15_MUTEX_ADAPTIVE) +#error "only one of X15_MUTEX_PI and X15_MUTEX_ADAPTIVE may be defined" +#endif + #if defined(X15_MUTEX_PI) #include +#elif defined(X15_MUTEX_ADAPTIVE) +#include #else #include #endif diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c new file mode 100644 index 0000000..f4274ce --- /dev/null +++ b/kern/mutex/mutex_adaptive.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Agustina Arzille. + * + * 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 3 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 + +static struct thread * +mutex_adaptive_get_thread(uintptr_t owner) +{ + return (struct thread *)(owner & ~MUTEX_ADAPTIVE_CONTENDED); +} + +static void +mutex_adaptive_set_contended(struct mutex *mutex) +{ + atomic_or_acq_rel(&mutex->owner, MUTEX_ADAPTIVE_CONTENDED); +} + +static inline bool +mutex_adaptive_is_owner(struct mutex *mutex, uintptr_t owner) +{ + uintptr_t prev; + + prev = atomic_load(&mutex->owner, ATOMIC_RELAXED); + return mutex_adaptive_get_thread(prev) == mutex_adaptive_get_thread(owner); +} + +void +mutex_adaptive_lock_slow(struct mutex *mutex) +{ + uintptr_t self, owner; + struct sleepq *sleepq; + unsigned long flags; + + self = (uintptr_t)thread_self(); + + sleepq = sleepq_lend(mutex, false, &flags); + + mutex_adaptive_set_contended(mutex); + + for (;;) { + owner = atomic_cas_acquire(&mutex->owner, MUTEX_ADAPTIVE_CONTENDED, + self | MUTEX_ADAPTIVE_CONTENDED); + assert(owner & MUTEX_ADAPTIVE_CONTENDED); + + if (mutex_adaptive_get_thread(owner) == NULL) { + break; + } + + /* + * The owner may not return from the unlock function if a thread is + * spinning on it. + */ + while (mutex_adaptive_is_owner(mutex, owner)) { + if (thread_is_running(mutex_adaptive_get_thread(owner))) { + cpu_pause(); + } else { + sleepq_wait(sleepq, "mutex"); + } + } + } + + /* + * A potentially spinning thread wouldn't be accounted in the sleep queue, + * but the only potentially spinning thread is the new owner. + */ + if (sleepq_empty(sleepq)) { + atomic_store(&mutex->owner, self, ATOMIC_RELAXED); + } + + sleepq_return(sleepq, flags); +} + +void +mutex_adaptive_unlock_slow(struct mutex *mutex) +{ + uintptr_t owner; + struct sleepq *sleepq; + unsigned long flags; + + atomic_store(&mutex->owner, MUTEX_ADAPTIVE_CONTENDED, ATOMIC_RELEASE); + + for (;;) { + owner = atomic_load(&mutex->owner, ATOMIC_RELAXED); + + /* + * This only happens if another thread was able to become the new + * owner, in which case that thread isn't spinning on the current + * thread, i.e. there is no need for an additional reference. + */ + if (owner != MUTEX_ADAPTIVE_CONTENDED) { + break; + } + + /* + * Avoid contending with incoming threads that are about to spin/wait + * on the mutex. This is particularly expensive with queued locks. + * + * Also, this call returns NULL if another thread is currently spinning + * on the current thread, in which case the latter doesn't return, + * averting the need for an additional reference. + */ + sleepq = sleepq_tryacquire(mutex, false, &flags); + + if (sleepq != NULL) { + sleepq_signal(sleepq); + sleepq_release(sleepq, flags); + break; + } + + /* + * Acquiring the sleep queue may fail because of contention on + * unrelated objects. Retry. + */ + } +} diff --git a/kern/mutex/mutex_adaptive_i.h b/kern/mutex/mutex_adaptive_i.h new file mode 100644 index 0000000..2dd0679 --- /dev/null +++ b/kern/mutex/mutex_adaptive_i.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 Agustina Arzille. + * + * 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 3 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 _KERN_MUTEX_ADAPTIVE_I_H +#define _KERN_MUTEX_ADAPTIVE_I_H + +#ifndef _KERN_MUTEX_H +#error "don't include directly," \ + " use instead" +#endif + +#include + +#include +#include +#include +#include +#include +#include + +/* + * Mutex flags. + * + * The "contended" flag indicates that threads are waiting for the mutex + * to be unlocked, potentially spinning on the owner. It forces threads + * trying to lock the mutex as well as the owner to take the slow path. + */ +#define MUTEX_ADAPTIVE_CONTENDED 0x1 + +static inline void +mutex_adaptive_init(struct mutex *mutex) +{ + mutex->owner = 0; +} + +#define mutex_adaptive_assert_locked(mutex) assert((mutex)->owner != 0) + +static inline int +mutex_adaptive_lock_fast(struct mutex *mutex) +{ + uintptr_t owner; + + owner = atomic_cas_acquire(&mutex->owner, 0, (uintptr_t)thread_self()); + + if (unlikely(owner != 0)) { + return ERROR_BUSY; + } + + return 0; +} + +static inline int +mutex_adaptive_unlock_fast(struct mutex *mutex) +{ + uintptr_t owner; + + owner = atomic_cas_release(&mutex->owner, (uintptr_t)thread_self(), 0); + + if (unlikely(owner & MUTEX_ADAPTIVE_CONTENDED)) { + return ERROR_BUSY; + } + + return 0; +} + +void mutex_adaptive_lock_slow(struct mutex *mutex); +void mutex_adaptive_unlock_slow(struct mutex *mutex); + +/* + * Interface exported to the public mutex header. + */ + +#define mutex_impl_init mutex_adaptive_init +#define mutex_impl_assert_locked mutex_adaptive_assert_locked + +static inline int +mutex_impl_trylock(struct mutex *mutex) +{ + return mutex_adaptive_lock_fast(mutex); +} + +static inline void +mutex_impl_lock(struct mutex *mutex) +{ + int error; + + error = mutex_adaptive_lock_fast(mutex); + + if (unlikely(error)) { + mutex_adaptive_lock_slow(mutex); + } +} + +static inline void +mutex_impl_unlock(struct mutex *mutex) +{ + int error; + + error = mutex_adaptive_unlock_fast(mutex); + + if (unlikely(error)) { + mutex_adaptive_unlock_slow(mutex); + } +} + +#endif /* _KERN_MUTEX_ADAPTIVE_I_H */ diff --git a/kern/mutex/mutex_adaptive_types.h b/kern/mutex/mutex_adaptive_types.h new file mode 100644 index 0000000..efbbf21 --- /dev/null +++ b/kern/mutex/mutex_adaptive_types.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Agustina Arzille. + * + * 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _KERN_MUTEX_ADAPTIVE_TYPES_H +#define _KERN_MUTEX_ADAPTIVE_TYPES_H + +#ifndef _KERN_MUTEX_TYPES_H +#error "don't include directly," \ + " use instead" +#endif + +#include + +struct mutex { + uintptr_t owner; +}; + +#endif /* _KERN_MUTEX_ADAPTIVE_TYPES_H */ diff --git a/kern/mutex_types.h b/kern/mutex_types.h index 432eab3..eb2bc33 100644 --- a/kern/mutex_types.h +++ b/kern/mutex_types.h @@ -23,6 +23,8 @@ #if defined(X15_MUTEX_PI) #include +#elif defined(X15_MUTEX_ADAPTIVE) +#include #else #include #endif diff --git a/kern/sleepq.c b/kern/sleepq.c index 09548db..5af3d06 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -257,6 +257,33 @@ sleepq_acquire(const void *sync_obj, bool condition, unsigned long *flags) return sleepq; } +struct sleepq * +sleepq_tryacquire(const void *sync_obj, bool condition, unsigned long *flags) +{ + struct sleepq_bucket *bucket; + struct sleepq *sleepq; + int error; + + assert(sync_obj != NULL); + + bucket = sleepq_bucket_get(sync_obj, condition); + + error = spinlock_trylock_intr_save(&bucket->lock, flags); + + if (error) { + return NULL; + } + + sleepq = sleepq_bucket_lookup(bucket, sync_obj); + + if (sleepq == NULL) { + spinlock_unlock_intr_restore(&bucket->lock, *flags); + return NULL; + } + + return sleepq; +} + void sleepq_release(struct sleepq *sleepq, unsigned long flags) { diff --git a/kern/sleepq.h b/kern/sleepq.h index e1f7863..f55d8c4 100644 --- a/kern/sleepq.h +++ b/kern/sleepq.h @@ -68,9 +68,15 @@ void sleepq_destroy(struct sleepq *sleepq); * * The condition argument must be true if the synchronization object * is a condition variable. + * + * Note that, in the case of the non-blocking variant, the call may also + * return NULL if internal state shared by unrelated synchronization + * objects is locked. */ struct sleepq * sleepq_acquire(const void *sync_obj, bool condition, unsigned long *flags); +struct sleepq * sleepq_tryacquire(const void *sync_obj, bool condition, + unsigned long *flags); void sleepq_release(struct sleepq *sleepq, unsigned long flags); /* diff --git a/kern/thread.c b/kern/thread.c index e687558..7ce22fb 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -551,7 +551,7 @@ thread_runq_get_next(struct thread_runq *runq) thread = thread_sched_ops[i].get_next(runq); if (thread != NULL) { - runq->current = thread; + atomic_store(&runq->current, thread, ATOMIC_RELAXED); return thread; } } @@ -571,7 +571,7 @@ thread_runq_set_next(struct thread_runq *runq, struct thread *thread) ops->set_next(runq, thread); } - runq->current = thread; + atomic_store(&runq->current, thread, ATOMIC_RELAXED); } static void @@ -2736,3 +2736,14 @@ thread_key_create(unsigned int *keyp, thread_dtor_fn_t dtor) thread_dtors[key] = dtor; *keyp = key; } + +bool +thread_is_running(const struct thread *thread) +{ + const struct thread_runq *runq; + + runq = thread->runq; + + return (runq != NULL) + && (atomic_load(&runq->current, ATOMIC_RELAXED) == thread); +} diff --git a/kern/thread.h b/kern/thread.h index 6270826..4d85c59 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -724,4 +724,12 @@ thread_get_specific(unsigned int key) return thread_tsd_get(thread_self(), key); } +/* + * Return true if the given thread is running. + * + * Note that this check is speculative, and may not return an accurate + * result. It may only be used for optimistic optimizations. + */ +bool thread_is_running(const struct thread *thread); + #endif /* _KERN_THREAD_H */ -- cgit v1.2.3 From 0c48c48b05b12b5d4faa966c74486ba3b1d3f06c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 21 Jul 2017 00:56:55 +0200 Subject: kern/kernel: fix test starting Commit b2ad7d862388558556288877a65f2797528168f4 made the switch to initialization operations, but introduced a regression by not creating an init operation for a compiled test. --- kern/kernel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kern/kernel.c b/kern/kernel.c index 434297d..755f42e 100644 --- a/kern/kernel.c +++ b/kern/kernel.c @@ -19,6 +19,7 @@ #include #include #include +#include #include void __init @@ -29,6 +30,10 @@ kernel_main(void) init_setup(); vm_page_log_info(); +#ifdef X15_RUN_TEST_MODULE + test_setup(); +#endif /* X15_RUN_TEST_MODULE */ + /* * Enabling application processors is done late in the boot process for * two reasons : -- cgit v1.2.3 From 6e16ff5a6002400489349f55cc7961bccf2fe520 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 21 Jul 2017 01:03:22 +0200 Subject: INSTALL: document the --enable-mutex-adaptive option --- INSTALL | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/INSTALL b/INSTALL index 3ec3eca..6777e7a 100644 --- a/INSTALL +++ b/INSTALL @@ -243,6 +243,13 @@ X15 Options in a file named `test_pmap_update_mp.c' would be selected with the option `--enable-test-module=pmap_update_mp'. +`--enable-mutex-adaptive' + Enable the adaptive spinning mutex implementation for regular mutexes. + Adaptive spinning mutexes optimistically spin instead of sleep if the + owner of a mutex is found running, in the hope the mutex is unlocked + soon. This optimization increases performance by avoiding some sleeps + and wakeups, but it makes the system globally non real-time. + `--enable-mutex-pi' Enable priority inheritance for regular mutexes (note that priority inheritance is always enabled for real-time mutexes). This option is -- cgit v1.2.3 From f504f743a969a82012a8c60283fe130c1dc5d74f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 24 Jul 2017 23:30:11 +0200 Subject: kern/cbuf: update from upstream --- kern/cbuf.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- kern/cbuf.h | 31 +++++++++++++++++++++++++++---- kern/console.c | 4 ++-- kern/log.c | 2 +- 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index ea848c5..95dd20f 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -52,16 +52,58 @@ cbuf_update_start(struct cbuf *cbuf) } } -void -cbuf_push(struct cbuf *cbuf, char byte) +int +cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase) +{ + size_t free_size; + + if (!erase) { + free_size = cbuf_capacity(cbuf) - cbuf_size(cbuf); + + if (size > free_size) { + return ERROR_AGAIN; + } + } + + return cbuf_write(cbuf, cbuf_end(cbuf), buf, size); +} + +int +cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep) { + int error; + + if (cbuf_size(cbuf) == 0) { + return ERROR_AGAIN; + } + + error = cbuf_read(cbuf, cbuf_start(cbuf), buf, sizep); + assert(!error); + cbuf->start += *sizep; + return 0; +} + +int +cbuf_pushb(struct cbuf *cbuf, char byte, bool erase) +{ + size_t free_size; + + if (!erase) { + free_size = cbuf_capacity(cbuf) - cbuf_size(cbuf); + + if (free_size == 0) { + return ERROR_AGAIN; + } + } + cbuf->buf[cbuf_index(cbuf, cbuf->end)] = byte; cbuf->end++; cbuf_update_start(cbuf); + return 0; } int -cbuf_pop(struct cbuf *cbuf, char *bytep) +cbuf_popb(struct cbuf *cbuf, char *bytep) { if (cbuf_size(cbuf) == 0) { return ERROR_AGAIN; diff --git a/kern/cbuf.h b/kern/cbuf.h index 4d69334..726435d 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -83,13 +83,36 @@ cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end) */ void cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity); +/* + * Append a buffer to a circular buffer. + * + * If erasing old data is not allowed, and the circular buffer doesn't have + * enough unused bytes for the new data, ERROR_AGAIN is returned. Otherwise, + * the end index is increased by the new data size, possibly erasing old + * data, in which case, the start index is updated accordingly. + */ +int cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase); + +/* + * Read bytes from a circular buffer. + * + * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest + * bytes are stored into the given buffer. On entry, the sizep argument points + * to the size of the given buffer. On exit, that value is updated to the + * number of bytes actually stored. If successful, the start index is increased + * by the amount of bytes read. + */ +int cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep); + /* * Append a byte to a circular buffer. * - * The end index is incremented. If the buffer is full, the oldest byte - * is overwritten and the start index is updated accordingly. + * If erasing old data is not allowed, and the circular buffer is full, + * ERROR_AGAIN is returned. Otherwise, the end index is incremented and, if the + * buffer is full, the oldest byte is overwritten and the start index + * is updated accordingly. */ -void cbuf_push(struct cbuf *cbuf, char byte); +int cbuf_pushb(struct cbuf *cbuf, char byte, bool erase); /* * Read a byte from a circular buffer. @@ -98,7 +121,7 @@ void cbuf_push(struct cbuf *cbuf, char byte); * byte is stored at the bytep address, the start index is incremented, * and 0 is returned. */ -int cbuf_pop(struct cbuf *cbuf, char *bytep); +int cbuf_popb(struct cbuf *cbuf, char *bytep); /* * Write into a circular buffer at a specific location. diff --git a/kern/console.c b/kern/console.c index 1f9a8a9..ebbdb80 100644 --- a/kern/console.c +++ b/kern/console.c @@ -109,7 +109,7 @@ console_getc(struct console *console) console->waiter = thread_self(); for (;;) { - error = cbuf_pop(&console->recvbuf, &c); + error = cbuf_popb(&console->recvbuf, &c); if (!error) { error = console_process_ctrl_char(console, c); @@ -186,7 +186,7 @@ console_intr(struct console *console, const char *s) goto out; } - cbuf_push(&console->recvbuf, *s); + cbuf_pushb(&console->recvbuf, *s, false); s++; } diff --git a/kern/log.c b/kern/log.c index 3d6ee37..a48afc5 100644 --- a/kern/log.c +++ b/kern/log.c @@ -461,7 +461,7 @@ log_write(const void *s, size_t size) { int error; - error = cbuf_write(&log_cbuf, cbuf_end(&log_cbuf), s, size); + error = cbuf_push(&log_cbuf, s, size, true); assert(!error); if (!cbuf_range_valid(&log_cbuf, log_index, log_index + 1)) { -- cgit v1.2.3 From 58144c76b6e8600999f8cd401c7a89ae8f55f947 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 24 Jul 2017 23:55:52 +0200 Subject: kern/list: update from upstream --- kern/list.h | 129 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/kern/list.h b/kern/list.h index c31b71a..eeb2934 100644 --- a/kern/list.h +++ b/kern/list.h @@ -15,7 +15,7 @@ * along with this program. If not, see . * * - * Simple doubly-linked list. + * Doubly-linked list. */ #ifndef _KERN_LIST_H @@ -58,7 +58,7 @@ list_init(struct list *list) /* * Initialize a list node. * - * An entry is in no lists when its node members point to NULL. + * A node is in no list when its node members point to NULL. */ static inline void list_node_init(struct list *node) @@ -68,7 +68,7 @@ list_node_init(struct list *node) } /* - * Return true if node is in no lists. + * Return true if node is in no list. */ static inline bool list_node_unlinked(const struct list *node) @@ -76,12 +76,6 @@ list_node_unlinked(const struct list *node) return node->prev == NULL; } -/* - * Macro that evaluates to the address of the structure containing the - * given node based on the given type and member. - */ -#define list_entry(node, type, member) structof(node, type, member) - /* * Return the first node of a list. */ @@ -119,31 +113,7 @@ list_prev(const struct list *node) } /* - * Get the first entry of a list. - */ -#define list_first_entry(list, type, member) \ - list_entry(list_first(list), type, member) - -/* - * Get the last entry of a list. - */ -#define list_last_entry(list, type, member) \ - list_entry(list_last(list), type, member) - -/* - * Get the entry next to the given entry. - */ -#define list_next_entry(entry, member) \ - list_entry(list_next(&(entry)->member), typeof(*(entry)), member) - -/* - * Get the entry previous to the given entry. - */ -#define list_prev_entry(entry, member) \ - list_entry(list_prev(&(entry)->member), typeof(*(entry)), member) - -/* - * Return true if node is after the last or before the first node of the list. + * Return true if node is invalid and denotes one of the ends of the list. */ static inline bool list_end(const struct list *list, const struct list *node) @@ -304,6 +274,36 @@ list_remove(struct list *node) node->next->prev = node->prev; } +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define list_entry(node, type, member) structof(node, type, member) + +/* + * Get the first entry of a list. + */ +#define list_first_entry(list, type, member) \ + list_entry(list_first(list), type, member) + +/* + * Get the last entry of a list. + */ +#define list_last_entry(list, type, member) \ + list_entry(list_last(list), type, member) + +/* + * Get the entry next to the given entry. + */ +#define list_next_entry(entry, member) \ + list_entry(list_next(&(entry)->member), typeof(*(entry)), member) + +/* + * Get the entry previous to the given entry. + */ +#define list_prev_entry(entry, member) \ + list_entry(list_prev(&(entry)->member), typeof(*(entry)), member) + /* * Forge a loop to process all nodes of a list. * @@ -381,13 +381,6 @@ for (entry = list_last_entry(list, typeof(*entry), member), \ * In addition, list_end() is also allowed in read-side critical sections. */ -/* - * Macro that evaluates to the address of the structure containing the - * given node based on the given type and member. - */ -#define list_llsync_entry(node, type, member) \ - structof(llsync_read_ptr(node), type, member) - /* * Return the first node of a list. */ @@ -406,24 +399,6 @@ list_llsync_next(const struct list *node) return llsync_read_ptr(node->next); } -/* - * Get the first entry of a list. - * - * Unlike list_entry(), this macro may evaluate to NULL, because the node - * pointer can only be read once, preventing the combination of lockless - * list_empty()/list_first_entry() variants. - * - * Return NULL if the list is empty. - */ -#define list_llsync_first_entry(list, type, member) \ -MACRO_BEGIN \ - struct list *___list = (list); \ - struct list *___first = llsync_read_ptr(___list->next); \ - list_end(___list, ___first) \ - ? NULL \ - : list_entry(___first, type, member); \ -MACRO_END - /* * Add a node between two nodes. * @@ -486,6 +461,42 @@ list_llsync_remove(struct list *node) llsync_assign_ptr(node->prev->next, node->next); } +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define list_llsync_entry(node, type, member) \ + structof(llsync_read_ptr(node), type, member) + +/* + * Get the first entry of a list. + * + * Unlike list_first_entry(), this macro may evaluate to NULL, because + * the node pointer can only be read once, preventing the combination + * of lockless list_empty()/list_first_entry() variants. + */ +#define list_llsync_first_entry(list, type, member) \ +MACRO_BEGIN \ + struct list *___list; \ + struct list *___first; \ + \ + ___list = (list); \ + ___first = list_llsync_first(___list); \ + list_end(___list, ___first) \ + ? NULL \ + : list_entry(___first, type, member); \ +MACRO_END + +/* + * Get the entry next to the given entry. + * + * Unlike list_next_entry(), this macro may evaluate to NULL, because + * the node pointer can only be read once, preventing the combination + * of lockless list_empty()/list_next_entry() variants. + */ +#define list_llsync_next_entry(entry, member) \ + list_llsync_first_entry(&entry->member, typeof(*entry), member) + /* * Forge a loop to process all nodes of a list. * -- cgit v1.2.3 From ba0a8610ca5093d81de618efd847298a613758f1 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 00:09:11 +0200 Subject: kern/hlist: new module --- Makefrag.am | 2 + kern/hlist.h | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/hlist_types.h | 40 ++++++ 3 files changed, 428 insertions(+) create mode 100644 kern/hlist.h create mode 100644 kern/hlist_types.h diff --git a/Makefrag.am b/Makefrag.am index a99f822..51dadbe 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -29,6 +29,8 @@ x15_SOURCES += \ kern/error.c \ kern/error.h \ kern/hash.h \ + kern/hlist.h \ + kern/hlist_types.h \ kern/fmt.c \ kern/fmt.h \ kern/init.c \ diff --git a/kern/hlist.h b/kern/hlist.h new file mode 100644 index 0000000..0d5184b --- /dev/null +++ b/kern/hlist.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Doubly-linked list specialized for forward traversals and O(1) removals. + */ + +#ifndef _KERN_HLIST_H +#define _KERN_HLIST_H + +#include +#include + +#include +#include +#include + +/* + * Static list initializer. + */ +#define HLIST_INITIALIZER(list) { NULL } + +/* + * Initialize a list. + */ +static inline void +hlist_init(struct hlist *list) +{ + list->first = NULL; +} + +/* + * Initialize a list node. + * + * A node is in no list when its pprev member points to NULL. + */ +static inline void +hlist_node_init(struct hlist_node *node) +{ + node->pprev = NULL; +} + +/* + * Return true if node is in no list. + */ +static inline bool +hlist_node_unlinked(const struct hlist_node *node) +{ + return node->pprev == NULL; +} + +/* + * Return the first node of a list. + */ +static inline struct hlist_node * +hlist_first(const struct hlist *list) +{ + return list->first; +} + +/* + * Return the node next to the given node. + */ +static inline struct hlist_node * +hlist_next(const struct hlist_node *node) +{ + return node->next; +} + +/* + * Return true if node is invalid and denotes the end of the list. + */ +static inline bool +hlist_end(const struct hlist_node *node) +{ + return node == NULL; +} + +/* + * Return true if list is empty. + */ +static inline bool +hlist_empty(const struct hlist *list) +{ + return list->first == NULL; +} + +/* + * Return true if list contains exactly one node. + */ +static inline bool +hlist_singular(const struct hlist *list) +{ + return !hlist_empty(list) && hlist_end(list->first->next); +} + +/* + * Set the new head of a list. + * + * After completion, old_head is stale. + */ +static inline void +hlist_set_head(struct hlist *new_head, const struct hlist *old_head) +{ + *new_head = *old_head; + + if (!hlist_empty(new_head)) { + new_head->first->pprev = &new_head->first; + } +} + +/* + * Insert a node at the head of a list. + */ +static inline void +hlist_insert_head(struct hlist *list, struct hlist_node *node) +{ + struct hlist_node *first; + + first = list->first; + node->next = first; + node->pprev = &list->first; + + if (first != NULL) { + first->pprev = &node->next; + } + + list->first = node; +} + +/* + * Insert a node before another node. + */ +static inline void +hlist_insert_before(struct hlist_node *next, struct hlist_node *node) +{ + node->next = next; + node->pprev = next->pprev; + next->pprev = &node->next; + *node->pprev = node; +} + +/* + * Insert a node after another node. + */ +static inline void +hlist_insert_after(struct hlist_node *prev, struct hlist_node *node) +{ + node->next = prev->next; + node->pprev = &prev->next; + + if (node->next != NULL) { + node->next->pprev = &node->next; + } + + prev->next = node; +} + +/* + * Remove a node from a list. + */ +static inline void +hlist_remove(struct hlist_node *node) +{ + if (node->next != NULL) { + node->next->pprev = node->pprev; + } + + *node->pprev = node->next; +} + +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define hlist_entry(node, type, member) structof(node, type, member) + +/* + * Get the first entry of a list. + */ +#define hlist_first_entry(list, type, member) \ +MACRO_BEGIN \ + struct hlist_node *___first; \ + \ + ___first = (list)->first; \ + hlist_end(___first) ? NULL : hlist_entry(___first, type, member); \ +MACRO_END + +/* + * Get the entry next to the given entry. + */ +#define hlist_next_entry(entry, member) \ +MACRO_BEGIN \ + struct hlist_node *___next; \ + \ + ___next = (entry)->member.next; \ + hlist_end(___next) \ + ? NULL \ + : hlist_entry(___next, typeof(*entry), member); \ +MACRO_END + +/* + * Forge a loop to process all nodes of a list. + * + * The node must not be altered during the loop. + */ +#define hlist_for_each(list, node) \ +for (node = hlist_first(list); \ + !hlist_end(node); \ + node = hlist_next(node)) + +/* + * Forge a loop to process all nodes of a list. + */ +#define hlist_for_each_safe(list, node, tmp) \ +for (node = hlist_first(list), \ + tmp = hlist_end(node) ? NULL : hlist_next(node); \ + !hlist_end(node); \ + node = tmp, \ + tmp = hlist_end(node) ? NULL : hlist_next(node)) + +/* + * Forge a loop to process all entries of a list. + * + * The entry node must not be altered during the loop. + */ +#define hlist_for_each_entry(list, entry, member) \ +for (entry = hlist_first_entry(list, typeof(*entry), member); \ + entry != NULL; \ + entry = hlist_next_entry(entry, member)) + +/* + * Forge a loop to process all entries of a list. + */ +#define hlist_for_each_entry_safe(list, entry, tmp, member) \ +for (entry = hlist_first_entry(list, typeof(*entry), member), \ + tmp = (entry == NULL) ? NULL : hlist_next_entry(entry, member); \ + entry != NULL; \ + entry = tmp, \ + tmp = (entry == NULL) ? NULL : hlist_next_entry(entry, member)) \ + +/* + * Lockless variants + * + * The hlist_end() function may be used from read-side critical sections. + */ + +/* + * Return the first node of a list. + */ +static inline struct hlist_node * +hlist_llsync_first(const struct hlist *list) +{ + return llsync_read_ptr(list->first); +} + +/* + * Return the node next to the given node. + */ +static inline struct hlist_node * +hlist_llsync_next(const struct hlist_node *node) +{ + return llsync_read_ptr(node->next); +} + +/* + * Insert a node at the head of a list. + */ +static inline void +hlist_llsync_insert_head(struct hlist *list, struct hlist_node *node) +{ + struct hlist_node *first; + + first = list->first; + node->next = first; + node->pprev = &list->first; + + if (first != NULL) { + first->pprev = &node->next; + } + + llsync_assign_ptr(list->first, node); +} + +/* + * Insert a node before another node. + */ +static inline void +hlist_llsync_insert_before(struct hlist_node *next, struct hlist_node *node) +{ + node->next = next; + node->pprev = next->pprev; + next->pprev = &node->next; + llsync_assign_ptr(*node->pprev, node); +} + +/* + * Insert a node after another node. + */ +static inline void +hlist_llsync_insert_after(struct hlist_node *prev, struct hlist_node *node) +{ + node->next = prev->next; + node->pprev = &prev->next; + + if (node->next != NULL) { + node->next->pprev = &node->next; + } + + llsync_assign_ptr(prev->next, node); +} + +/* + * Remove a node from a list. + */ +static inline void +hlist_llsync_remove(struct hlist_node *node) +{ + if (node->next != NULL) { + node->next->pprev = node->pprev; + } + + llsync_assign_ptr(*node->pprev, node->next); +} + +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define hlist_llsync_entry(node, type, member) \ + structof(llsync_read_ptr(node), type, member) + +/* + * Get the first entry of a list. + */ +#define hlist_llsync_first_entry(list, type, member) \ +MACRO_BEGIN \ + struct hlist_node *___first; \ + \ + ___first = hlist_llsync_first(list); \ + hlist_end(___first) ? NULL : hlist_entry(___first, type, member); \ +MACRO_END + +/* + * Get the entry next to the given entry. + */ +#define hlist_llsync_next_entry(entry, member) \ +MACRO_BEGIN \ + struct hlist_node *___next; \ + \ + ___next = hlist_llsync_next(&entry->member); \ + hlist_end(___next) \ + ? NULL \ + : hlist_entry(___next, typeof(*entry), member); \ +MACRO_END + +/* + * Forge a loop to process all nodes of a list. + */ +#define hlist_llsync_for_each(list, node) \ +for (node = hlist_llsync_first(list); \ + !hlist_end(node); \ + node = hlist_llsync_next(node)) + +/* + * Forge a loop to process all entries of a list. + */ +#define hlist_llsync_for_each_entry(list, entry, member) \ +for (entry = hlist_llsync_first_entry(list, typeof(*entry), member); \ + entry != NULL; \ + entry = hlist_llsync_next_entry(entry, member)) + +#endif /* _KERN_HLIST_H */ diff --git a/kern/hlist_types.h b/kern/hlist_types.h new file mode 100644 index 0000000..f50b62d --- /dev/null +++ b/kern/hlist_types.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _KERN_HLIST_TYPES_H +#define _KERN_HLIST_TYPES_H + +/* + * List node. + * + * The pprev member points to another node member instead of another node, + * so that it may safely refer to the first member of the list head. Its + * main purpose is to allow O(1) removal. + */ +struct hlist_node { + struct hlist_node *next; + struct hlist_node **pprev; +}; + +struct hlist { + struct hlist_node *first; +}; + +#endif /* _KERN_HLIST_TYPES_H */ -- cgit v1.2.3 From 02110f5991676b2ba8cb8d4f191997d74e92b7fb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 00:32:56 +0200 Subject: kern/slist: new module --- Makefrag.am | 2 + kern/slist.h | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/slist_types.h | 38 +++++ 3 files changed, 482 insertions(+) create mode 100644 kern/slist.h create mode 100644 kern/slist_types.h diff --git a/Makefrag.am b/Makefrag.am index 51dadbe..8c2e35e 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -87,6 +87,8 @@ x15_SOURCES += \ kern/shutdown.h \ kern/sleepq.c \ kern/sleepq.h \ + kern/slist.h \ + kern/slist_types.h \ kern/spinlock.c \ kern/spinlock.h \ kern/spinlock_i.h \ diff --git a/kern/slist.h b/kern/slist.h new file mode 100644 index 0000000..8d42db2 --- /dev/null +++ b/kern/slist.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Singly-linked list. + */ + +#ifndef _KERN_SLIST_H +#define _KERN_SLIST_H + +#include +#include + +#include +#include +#include + +/* + * Static list initializer. + */ +#define SLIST_INITIALIZER(list) { NULL, NULL } + +/* + * Initialize a list. + */ +static inline void +slist_init(struct slist *list) +{ + list->first = NULL; + list->last = NULL; +} + +/* + * Return the first node of a list. + */ +static inline struct slist_node * +slist_first(const struct slist *list) +{ + return list->first; +} + +/* + * Return the last node of a list. + */ +static inline struct slist_node * +slist_last(const struct slist *list) +{ + return list->last; +} + +/* + * Return the node next to the given node. + */ +static inline struct slist_node * +slist_next(const struct slist_node *node) +{ + return node->next; +} + +/* + * Return true if node is invalid and denotes one of the ends of the list. + */ +static inline bool +slist_end(const struct slist_node *node) +{ + return node == NULL; +} + +/* + * Return true if list is empty. + */ +static inline bool +slist_empty(const struct slist *list) +{ + return list->first == NULL; +} + +/* + * Return true if list contains exactly one node. + */ +static inline bool +slist_singular(const struct slist *list) +{ + return !slist_empty(list) && (list->first == list->last); +} + +/* + * Append the nodes of list2 at the end of list1. + * + * After completion, list2 is stale. + */ +static inline void +slist_concat(struct slist *list1, const struct slist *list2) +{ + if (slist_empty(list2)) { + return; + } + + if (slist_empty(list1)) { + list1->first = list2->first; + } else { + list1->last->next = list2->first; + } + + list1->last = list2->last; +} + +/* + * Set the new head of a list. + * + * This function is an optimized version of : + * list_init(&new_list); + * list_concat(&new_list, &old_list); + */ +static inline void +slist_set_head(struct slist *new_head, const struct slist *old_head) +{ + *new_head = *old_head; +} + +/* + * Insert a node at the head of a list. + */ +static inline void +slist_insert_head(struct slist *list, struct slist_node *node) +{ + if (slist_empty(list)) { + list->last = node; + } + + node->next = list->first; + list->first = node; +} + +/* + * Insert a node at the tail of a list. + */ +static inline void +slist_insert_tail(struct slist *list, struct slist_node *node) +{ + node->next = NULL; + + if (slist_empty(list)) { + list->first = node; + } else { + list->last->next = node; + } + + list->last = node; +} + +/* + * Insert a node after another node. + * + * The prev node must be valid. + */ +static inline void +slist_insert_after(struct slist *list, struct slist_node *prev, + struct slist_node *node) +{ + node->next = prev->next; + prev->next = node; + + if (list->last == prev) { + list->last = node; + } +} + +/* + * Remove a node from a list. + * + * The prev argument must point to the node immediately preceding the target + * node. It may safely denote the end of the given list, in which case the + * first node is removed. + */ +static inline void +slist_remove(struct slist *list, struct slist_node *prev) +{ + struct slist_node *node; + + if (slist_end(prev)) { + node = list->first; + list->first = node->next; + + if (list->last == node) { + list->last = NULL; + } + } else { + node = prev->next; + prev->next = node->next; + + if (list->last == node) { + list->last = prev; + } + } +} + +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define slist_entry(node, type, member) structof(node, type, member) + +/* + * Get the first entry of a list. + */ +#define slist_first_entry(list, type, member) \ +MACRO_BEGIN \ + struct slist_node *___first; \ + \ + ___first = (list)->first; \ + slist_end(___first) ? NULL : slist_entry(___first, type, member); \ +MACRO_END + +/* + * Get the last entry of a list. + */ +#define slist_last_entry(list, type, member) \ +MACRO_BEGIN \ + struct slist_node *___last; \ + \ + ___last = (list)->last; \ + slist_end(___last) ? NULL : slist_entry(___last, type, member); \ +MACRO_END + +/* + * Get the entry next to the given entry. + */ +#define slist_next_entry(entry, member) \ +MACRO_BEGIN \ + struct slist_node *___next; \ + \ + ___next = (entry)->member.next; \ + slist_end(___next) \ + ? NULL \ + : slist_entry(___next, typeof(*entry), member); \ +MACRO_END + +/* + * Forge a loop to process all nodes of a list. + * + * The node must not be altered during the loop. + */ +#define slist_for_each(list, node) \ +for (node = slist_first(list); \ + !slist_end(node); \ + node = slist_next(node)) + +/* + * Forge a loop to process all nodes of a list. + */ +#define slist_for_each_safe(list, node, tmp) \ +for (node = slist_first(list), \ + tmp = slist_end(node) ? NULL : slist_next(node); \ + !slist_end(node); \ + node = tmp, \ + tmp = slist_end(node) ? NULL : slist_next(node)) + +/* + * Forge a loop to process all entries of a list. + * + * The entry node must not be altered during the loop. + */ +#define slist_for_each_entry(list, entry, member) \ +for (entry = slist_first_entry(list, typeof(*entry), member); \ + entry != NULL; \ + entry = slist_next_entry(entry, member)) + +/* + * Forge a loop to process all entries of a list. + */ +#define slist_for_each_entry_safe(list, entry, tmp, member) \ +for (entry = slist_first_entry(list, typeof(*entry), member), \ + tmp = (entry == NULL) ? NULL : slist_next_entry(entry, member); \ + entry != NULL; \ + entry = tmp, \ + tmp = (entry == NULL) ? NULL : slist_next_entry(entry, member)) \ + +/* + * Lockless variants + * + * The slist_end() function may be used from read-side critical sections. + */ + +/* + * Return the first node of a list. + */ +static inline struct slist_node * +slist_llsync_first(const struct slist *list) +{ + return llsync_read_ptr(list->first); +} + +/* + * Return the node next to the given node. + */ +static inline struct slist_node * +slist_llsync_next(const struct slist_node *node) +{ + return llsync_read_ptr(node->next); +} + +/* + * Insert a node at the head of a list. + */ +static inline void +slist_llsync_insert_head(struct slist *list, struct slist_node *node) +{ + if (slist_empty(list)) { + list->last = node; + } + + node->next = list->first; + llsync_assign_ptr(list->first, node); +} + +/* + * Insert a node at the tail of a list. + */ +static inline void +slist_llsync_insert_tail(struct slist *list, struct slist_node *node) +{ + node->next = NULL; + + if (slist_empty(list)) { + llsync_assign_ptr(list->first, node); + } else { + llsync_assign_ptr(list->last->next, node); + } + + list->last = node; +} + +/* + * Insert a node after another node. + * + * The prev argument is used to determine the insertion point. It may safely + * denote the end of the given list, in which case the node is inserted at + * the head of the list. + */ +static inline void +slist_llsync_insert_after(struct slist *list, struct slist_node *prev, + struct slist_node *node) +{ + node->next = prev->next; + llsync_assign_ptr(prev->next, node); + + if (list->last == prev) { + list->last = node; + } +} + +/* + * Remove a node from a list. + * + * The prev argument must point to the node immediately preceding the target + * node. It may safely denote the end of the given list, in which case the + * first node is removed. + */ +static inline void +slist_llsync_remove(struct slist *list, struct slist_node *prev) +{ + struct slist_node *node; + + if (slist_end(prev)) { + node = list->first; + llsync_assign_ptr(list->first, node->next); + + if (list->last == node) { + list->last = NULL; + } + } else { + node = prev->next; + llsync_assign_ptr(prev->next, node->next); + + if (list->last == node) { + list->last = prev; + } + } +} + +/* + * Macro that evaluates to the address of the structure containing the + * given node based on the given type and member. + */ +#define slist_llsync_entry(node, type, member) \ + structof(llsync_read_ptr(node), type, member) + +/* + * Get the first entry of a list. + */ +#define slist_llsync_first_entry(list, type, member) \ +MACRO_BEGIN \ + struct slist_node *___first; \ + \ + ___first = slist_llsync_first(list); \ + slist_end(___first) ? NULL : slist_entry(___first, type, member); \ +MACRO_END + +/* + * Get the entry next to the given entry. + */ +#define slist_llsync_next_entry(entry, member) \ +MACRO_BEGIN \ + struct slist_node *___next; \ + \ + ___next = slist_llsync_next(&entry->member); \ + slist_end(___next) \ + ? NULL \ + : slist_entry(___next, typeof(*entry), member); \ +MACRO_END + +/* + * Forge a loop to process all nodes of a list. + */ +#define slist_llsync_for_each(list, node) \ +for (node = slist_llsync_first(list); \ + !slist_end(node); \ + node = slist_llsync_next(node)) + +/* + * Forge a loop to process all entries of a list. + */ +#define slist_llsync_for_each_entry(list, entry, member) \ +for (entry = slist_llsync_first_entry(list, typeof(*entry), member); \ + entry != NULL; \ + entry = slist_llsync_next_entry(entry, member)) + +#endif /* _KERN_SLIST_H */ diff --git a/kern/slist_types.h b/kern/slist_types.h new file mode 100644 index 0000000..78d7ffb --- /dev/null +++ b/kern/slist_types.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Isolated type definition used to avoid inclusion circular dependencies. + */ + +#ifndef _KERN_SLIST_TYPES_H +#define _KERN_SLIST_TYPES_H + +#include +#include + +#include "macros.h" + +struct slist_node { + struct slist_node *next; +}; + +struct slist { + struct slist_node *first; + struct slist_node *last; +}; + +#endif /* _KERN_SLIST_TYPES_H */ -- cgit v1.2.3 From 34a60abd1ab042960a8b7fcb2cd78f24e79ae2a0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 01:18:45 +0200 Subject: doc/intro(9): mention the hash/hlist/slist/mutex implementation modules --- doc/asciidoc.conf | 9 +++++++++ doc/intro.9.txt | 24 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf index 49c3009..ee2bb5f 100644 --- a/doc/asciidoc.conf +++ b/doc/asciidoc.conf @@ -6,9 +6,18 @@ against-priority-inheritance=https://fsmlabs.com/pdfs/Priority_Inheritance.pdf[A the-art-of-unix-programming=http://www.catb.org/esr/writings/taoup/html/[The Art of Unix Programming] [macros] +(?su)(?Pmodule_mutex):(?P\w+)= (?su)(?Pmodule):(?P\w+)/(?P\w+)= (?su)(?Pmanpage):(?P\w+)= +[module_mutex-inlinemacro] +ifdef::basebackend-html[] +mutex_{impl} +endif::basebackend-html[] +ifdef::basebackend-docbook[] +mutex_{impl} +endif::basebackend-docbook[] + [module-inlinemacro] ifdef::basebackend-html[] {module} diff --git a/doc/intro.9.txt b/doc/intro.9.txt index e3eb4ee..8d9bb87 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -102,6 +102,21 @@ module:kern/thread:: module:kern/work:: Work queue of deferred asynchronous lightweight jobs. +Mutex implementations +~~~~~~~~~~~~~~~~~~~~~ + +In order to best satisfy either performance or real-time needs, the +kernel provides multiple mutex implementations. Note that, whatever +implementation is chosen, the *rtmutex* module is always available. +The mutex implementations are : + +module_mutex:adaptive:: + Adaptive spinning mutex. +module_mutex:pi:: + Real-time mutex with priority inheritance. +module_mutex:plain:: + Default mutex. + [[generic_development_tools]] GENERIC DEVELOPMENT TOOLS ------------------------- @@ -115,6 +130,10 @@ module:kern/cbuf:: Circular character buffer. module:kern/error:: Common errors and error handling functions. +module:kern/hash:: + Hash functions for integers and strings. +module:kern/hlist:: + Doubly-linked list specialized for forward traversals and O(1) removals. module:kern/kmem:: Object caching and general purpose memory allocator. module:kern/list:: @@ -127,6 +146,8 @@ module:kern/rbtree:: Red-black tree. module:kern/rdxtree:: Radix tree (with integer keys). +module:kern/slist:: + Singly-linked list. module:kern/sprintf:: Formatted string functions. module:kern/syscnt:: @@ -135,8 +156,7 @@ module:kern/syscnt:: X15 doesn't provide a generic queue interface, because the requirements often vary too much. Similarly, it doesn't provide a hash table interface. Instead, users can easily build specialized queues, hash tables and ring -buffers on top of the provided facilities. Hash functions may be provided -in the future. +buffers on top of the provided facilities. [[multiprocessor_support]] MULTIPROCESSOR SUPPORT -- cgit v1.2.3 From c0baee9625bd06fa58bb3137a107871746506d2f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:11:40 +0200 Subject: kern/cbuf: update from upstream --- kern/cbuf.c | 16 ++++++++++------ kern/cbuf.h | 56 +++++++++++++++++++++++++------------------------------- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/kern/cbuf.c b/kern/cbuf.c index 95dd20f..2093f42 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -27,7 +28,7 @@ #define CBUF_INIT_INDEX ((size_t)-500) void -cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity) +cbuf_init(struct cbuf *cbuf, void *buf, size_t capacity) { assert(ISP2(capacity)); @@ -84,7 +85,7 @@ cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep) } int -cbuf_pushb(struct cbuf *cbuf, char byte, bool erase) +cbuf_pushb(struct cbuf *cbuf, uint8_t byte, bool erase) { size_t free_size; @@ -103,13 +104,16 @@ cbuf_pushb(struct cbuf *cbuf, char byte, bool erase) } int -cbuf_popb(struct cbuf *cbuf, char *bytep) +cbuf_popb(struct cbuf *cbuf, void *bytep) { + uint8_t *ptr; + if (cbuf_size(cbuf) == 0) { return ERROR_AGAIN; } - *bytep = cbuf->buf[cbuf_index(cbuf, cbuf->start)]; + ptr = bytep; + *ptr = cbuf->buf[cbuf_index(cbuf, cbuf->start)]; cbuf->start++; return 0; } @@ -117,7 +121,7 @@ cbuf_popb(struct cbuf *cbuf, char *bytep) int cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size) { - char *start, *end, *buf_end; + uint8_t *start, *end, *buf_end; size_t new_end, skip; if (!cbuf_range_valid(cbuf, index, cbuf->end)) { @@ -157,7 +161,7 @@ cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size) int cbuf_read(const struct cbuf *cbuf, size_t index, void *buf, size_t *sizep) { - const char *start, *end, *buf_end; + const uint8_t *start, *end, *buf_end; size_t size; /* At least one byte must be available */ diff --git a/kern/cbuf.h b/kern/cbuf.h index 726435d..a64121d 100644 --- a/kern/cbuf.h +++ b/kern/cbuf.h @@ -15,7 +15,7 @@ * along with this program. If not, see . * * - * Circular character buffer. + * Circular byte buffer. */ #ifndef _KERN_CBUF_H @@ -23,6 +23,7 @@ #include #include +#include /* * Circular buffer descriptor. @@ -31,7 +32,7 @@ * which can overflow. Their difference cannot exceed the capacity. */ struct cbuf { - char *buf; + uint8_t *buf; size_t capacity; size_t start; size_t end; @@ -81,65 +82,58 @@ cbuf_range_valid(const struct cbuf *cbuf, size_t start, size_t end) * The descriptor is set to use the given buffer for storage. Capacity * must be a power-of-two. */ -void cbuf_init(struct cbuf *cbuf, char *buf, size_t capacity); +void cbuf_init(struct cbuf *cbuf, void *buf, size_t capacity); /* - * Append a buffer to a circular buffer. + * Push data to a circular buffer. * - * If erasing old data is not allowed, and the circular buffer doesn't have - * enough unused bytes for the new data, ERROR_AGAIN is returned. Otherwise, - * the end index is increased by the new data size, possibly erasing old - * data, in which case, the start index is updated accordingly. + * If the function isn't allowed to erase old data and the circular buffer + * doesn't have enough unused bytes for the new data, ERROR_AGAIN is returned. */ int cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase); /* - * Read bytes from a circular buffer. + * Pop data from a circular buffer. * - * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest - * bytes are stored into the given buffer. On entry, the sizep argument points - * to the size of the given buffer. On exit, that value is updated to the - * number of bytes actually stored. If successful, the start index is increased - * by the amount of bytes read. + * On entry, the sizep argument points to the size of the output buffer. + * On exit, it is updated to the number of bytes actually transferred. + * + * If the buffer is empty, ERROR_AGAIN is returned, and the size of the + * output buffer is undefined. */ int cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep); /* - * Append a byte to a circular buffer. + * Push a byte to a circular buffer. * - * If erasing old data is not allowed, and the circular buffer is full, - * ERROR_AGAIN is returned. Otherwise, the end index is incremented and, if the - * buffer is full, the oldest byte is overwritten and the start index - * is updated accordingly. + * If the function isn't allowed to erase old data and the circular buffer + * is full, ERROR_AGAIN is returned. */ -int cbuf_pushb(struct cbuf *cbuf, char byte, bool erase); +int cbuf_pushb(struct cbuf *cbuf, uint8_t byte, bool erase); /* - * Read a byte from a circular buffer. + * Pop a byte from a circular buffer. * - * If the buffer is empty, ERROR_AGAIN is returned. Otherwise, the oldest - * byte is stored at the bytep address, the start index is incremented, - * and 0 is returned. + * If the buffer is empty, ERROR_AGAIN is returned. */ -int cbuf_popb(struct cbuf *cbuf, char *bytep); +int cbuf_popb(struct cbuf *cbuf, void *bytep); /* * Write into a circular buffer at a specific location. * * If the given index is outside buffer boundaries, ERROR_INVAL is returned. - * Otherwise size bytes are copied into the circular buffer. If the range - * in the circular buffer goes beyond its end, the end index is updated as - * appropriate. If the buffer is full when extending its end, the oldest - * bytes are overwritten and the start index is updated accordingly. + * The given [index, size) range may extend beyond the end of the circular + * buffer. */ int cbuf_write(struct cbuf *cbuf, size_t index, const void *buf, size_t size); /* * Read from a circular buffer at a specific location. * + * On entry, the sizep argument points to the size of the output buffer. + * On exit, it is updated to the number of bytes actually transferred. + * * If the given index is outside buffer boundaries, ERROR_INVAL is returned. - * Otherwise at most *sizep bytes are copied into the given byte buffer, - * and *sizep is updated to the number of bytes actually copied. * * The circular buffer isn't changed by this operation. */ -- cgit v1.2.3 From 068796c5b7f8b1a21b09a18a8b66447c160df24d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:18:18 +0200 Subject: kern/init: implement stacks with a singly-linked list --- kern/init.c | 24 ++++++++++++------------ kern/init_i.h | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/kern/init.c b/kern/init.c index 1dc5114..6bd4838 100644 --- a/kern/init.c +++ b/kern/init.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -47,14 +47,14 @@ static_assert(sizeof(struct init_op) == INIT_OP_ALIGN, "invalid init_op size"); * This type is used for the output of the topological sort. */ struct init_ops_list { - struct list ops; + struct slist ops; size_t size; }; static void __init init_ops_list_init(struct init_ops_list *list) { - list_init(&list->ops); + slist_init(&list->ops); list->size = 0; } @@ -67,7 +67,7 @@ init_ops_list_size(const struct init_ops_list *list) static void __init init_ops_list_push(struct init_ops_list *list, struct init_op *op) { - list_insert_tail(&list->ops, &op->list_node); + slist_insert_head(&list->ops, &op->list_node); list->size++; } @@ -80,8 +80,8 @@ init_ops_list_pop(struct init_ops_list *list) return NULL; } - op = list_last_entry(&list->ops, struct init_op, list_node); - list_remove(&op->list_node); + op = slist_first_entry(&list->ops, struct init_op, list_node); + slist_remove(&list->ops, NULL); list->size--; return op; } @@ -92,19 +92,19 @@ init_ops_list_pop(struct init_ops_list *list) * This type is used internally by the topological sort algorithm. */ struct init_ops_stack { - struct list ops; + struct slist ops; }; static void __init init_ops_stack_init(struct init_ops_stack *stack) { - list_init(&stack->ops); + slist_init(&stack->ops); } static void __init init_ops_stack_push(struct init_ops_stack *stack, struct init_op *op) { - list_insert_tail(&stack->ops, &op->stack_node); + slist_insert_head(&stack->ops, &op->stack_node); } static struct init_op * __init @@ -112,12 +112,12 @@ init_ops_stack_pop(struct init_ops_stack *stack) { struct init_op *op; - if (list_empty(&stack->ops)) { + if (slist_empty(&stack->ops)) { return NULL; } - op = list_last_entry(&stack->ops, struct init_op, stack_node); - list_remove(&op->stack_node); + op = slist_first_entry(&stack->ops, struct init_op, stack_node); + slist_remove(&stack->ops, NULL); return op; } diff --git a/kern/init_i.h b/kern/init_i.h index be23073..7a6b463 100644 --- a/kern/init_i.h +++ b/kern/init_i.h @@ -28,7 +28,7 @@ #include #include -#include +#include #include #define __initop __section(QUOTE(INIT_OPS_SECTION)) @@ -38,8 +38,8 @@ #define INIT_OP_STATE_COMPLETE 2 struct init_op { - alignas(INIT_OP_ALIGN) struct list list_node; - struct list stack_node; + alignas(INIT_OP_ALIGN) struct slist_node list_node; + struct slist_node stack_node; const char *name; init_op_fn_t fn; struct init_op_dep *deps; -- cgit v1.2.3 From cdb1aa3fc9b7865481177e3f80f4565c941e541b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:32:39 +0200 Subject: doc/intro(9): improve description of mutex implementations --- doc/intro.9.txt | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 8d9bb87..e3dcfa3 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -105,17 +105,23 @@ module:kern/work:: Mutex implementations ~~~~~~~~~~~~~~~~~~~~~ -In order to best satisfy either performance or real-time needs, the -kernel provides multiple mutex implementations. Note that, whatever -implementation is chosen, the *rtmutex* module is always available. -The mutex implementations are : +In order to best satisfy either overall performance or latencies, the +kernel provides several mutex implementations for the interface provided +by the *mutex* module. Note that, whatever implementation is selected, +the *rtmutex* module is always available. The mutex implementations are : module_mutex:adaptive:: - Adaptive spinning mutex. + Adaptive spinning mutex, spinning instead of sleeping if the owner + is running, in the hope the critical section is short and the mutex + will be unlocked soon, to avoid expensive sleep/wakeup operations. + This implementation should improve overall performance at the cost + of increased latencies. module_mutex:pi:: - Real-time mutex with priority inheritance. + Real-time mutex with priority inheritance. This implementation is a + wrapper in front of the *rtmutex* module. It should improve latencies + at the cost of overall performance. module_mutex:plain:: - Default mutex. + Default mutex, immediately sleeping on contention. [[generic_development_tools]] GENERIC DEVELOPMENT TOOLS -- cgit v1.2.3 From a531b1cf1c6b41baae4927503782cda60574e09b Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:37:51 +0200 Subject: kern/fmt: fix license --- kern/fmt.c | 32 ++++++++++---------------------- kern/fmt.h | 30 +++++++++++------------------- 2 files changed, 21 insertions(+), 41 deletions(-) diff --git a/kern/fmt.c b/kern/fmt.c index d1067e5..03ababe 100644 --- a/kern/fmt.c +++ b/kern/fmt.c @@ -1,30 +1,18 @@ /* * Copyright (c) 2010-2017 Richard Braun. - * All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * 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 3 of the License, or + * (at your option) any later version. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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. * - * - * Upstream site with license notes : - * http://git.sceen.net/rbraun/librbraun.git/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include diff --git a/kern/fmt.h b/kern/fmt.h index a339ed4..9715d82 100644 --- a/kern/fmt.h +++ b/kern/fmt.h @@ -1,26 +1,18 @@ /* * Copyright (c) 2010-2017 Richard Braun. - * All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * 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 3 of the License, or + * (at your option) any later version. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 . * * * Formatted string functions. -- cgit v1.2.3 From 29d83e5c1b65949de3149c9e8da8cd3516377ec2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:40:37 +0200 Subject: kern/mutex/mutex_plain: improve atomic access --- kern/mutex/mutex_plain.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c index a925a5a..5e4ba53 100644 --- a/kern/mutex/mutex_plain.c +++ b/kern/mutex/mutex_plain.c @@ -43,8 +43,7 @@ mutex_plain_lock_slow(struct mutex *mutex) } if (sleepq_empty(sleepq)) { - /* TODO Review memory order */ - atomic_store(&mutex->state, MUTEX_LOCKED, ATOMIC_RELEASE); + atomic_store(&mutex->state, MUTEX_LOCKED, ATOMIC_RELAXED); } sleepq_return(sleepq, flags); -- cgit v1.2.3 From 05185d96f469bd997740ac4218f168721a4bd677 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 20:45:48 +0200 Subject: doc/intro(9): specify that the list module implements a doubly-linked list --- doc/intro.9.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index e3dcfa3..515edbf 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -143,7 +143,7 @@ module:kern/hlist:: module:kern/kmem:: Object caching and general purpose memory allocator. module:kern/list:: - Linked list. + Doubly-linked list. module:kern/macros:: Useful generic macros. module:kern/printf:: -- cgit v1.2.3 From 50134de5026e9cd5b4b744b78731025ce17bc3cb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 21:09:04 +0200 Subject: kern/llsync: rename pointer accessors --- kern/hlist.h | 14 +++++++------- kern/list.h | 10 +++++----- kern/llsync.h | 12 ++++++------ kern/rdxtree.c | 30 +++++++++++++++--------------- kern/rdxtree.h | 2 +- kern/slist.h | 18 +++++++++--------- test/test_llsync_defer.c | 6 +++--- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/kern/hlist.h b/kern/hlist.h index 0d5184b..eec05bb 100644 --- a/kern/hlist.h +++ b/kern/hlist.h @@ -264,7 +264,7 @@ for (entry = hlist_first_entry(list, typeof(*entry), member), \ static inline struct hlist_node * hlist_llsync_first(const struct hlist *list) { - return llsync_read_ptr(list->first); + return llsync_load_ptr(list->first); } /* @@ -273,7 +273,7 @@ hlist_llsync_first(const struct hlist *list) static inline struct hlist_node * hlist_llsync_next(const struct hlist_node *node) { - return llsync_read_ptr(node->next); + return llsync_load_ptr(node->next); } /* @@ -292,7 +292,7 @@ hlist_llsync_insert_head(struct hlist *list, struct hlist_node *node) first->pprev = &node->next; } - llsync_assign_ptr(list->first, node); + llsync_store_ptr(list->first, node); } /* @@ -304,7 +304,7 @@ hlist_llsync_insert_before(struct hlist_node *next, struct hlist_node *node) node->next = next; node->pprev = next->pprev; next->pprev = &node->next; - llsync_assign_ptr(*node->pprev, node); + llsync_store_ptr(*node->pprev, node); } /* @@ -320,7 +320,7 @@ hlist_llsync_insert_after(struct hlist_node *prev, struct hlist_node *node) node->next->pprev = &node->next; } - llsync_assign_ptr(prev->next, node); + llsync_store_ptr(prev->next, node); } /* @@ -333,7 +333,7 @@ hlist_llsync_remove(struct hlist_node *node) node->next->pprev = node->pprev; } - llsync_assign_ptr(*node->pprev, node->next); + llsync_store_ptr(*node->pprev, node->next); } /* @@ -341,7 +341,7 @@ hlist_llsync_remove(struct hlist_node *node) * given node based on the given type and member. */ #define hlist_llsync_entry(node, type, member) \ - structof(llsync_read_ptr(node), type, member) + structof(llsync_load_ptr(node), type, member) /* * Get the first entry of a list. diff --git a/kern/list.h b/kern/list.h index eeb2934..f48d7d7 100644 --- a/kern/list.h +++ b/kern/list.h @@ -387,7 +387,7 @@ for (entry = list_last_entry(list, typeof(*entry), member), \ static inline struct list * list_llsync_first(const struct list *list) { - return llsync_read_ptr(list->next); + return llsync_load_ptr(list->next); } /* @@ -396,7 +396,7 @@ list_llsync_first(const struct list *list) static inline struct list * list_llsync_next(const struct list *node) { - return llsync_read_ptr(node->next); + return llsync_load_ptr(node->next); } /* @@ -409,7 +409,7 @@ list_llsync_add(struct list *prev, struct list *next, struct list *node) { node->next = next; node->prev = prev; - llsync_assign_ptr(prev->next, node); + llsync_store_ptr(prev->next, node); next->prev = node; } @@ -458,7 +458,7 @@ static inline void list_llsync_remove(struct list *node) { node->next->prev = node->prev; - llsync_assign_ptr(node->prev->next, node->next); + llsync_store_ptr(node->prev->next, node->next); } /* @@ -466,7 +466,7 @@ list_llsync_remove(struct list *node) * given node based on the given type and member. */ #define list_llsync_entry(node, type, member) \ - structof(llsync_read_ptr(node), type, member) + structof(llsync_load_ptr(node), type, member) /* * Get the first entry of a list. diff --git a/kern/llsync.h b/kern/llsync.h index 3852b0b..4a0d026 100644 --- a/kern/llsync.h +++ b/kern/llsync.h @@ -38,8 +38,8 @@ * supported architectures must guarantee that, when updating a word, and * in turn a pointer, other processors reading that word obtain a valid * value, that is either the previous or the next value of the word, but not - * a mixed-up value. The llsync module provides the llsync_assign_ptr() and - * llsync_read_ptr() wrappers that take care of low level details such as + * a mixed-up value. The llsync module provides the llsync_store_ptr() and + * llsync_load_ptr() wrappers that take care of low level details such as * compiler and memory barriers, so that objects are completely built and * consistent when published and accessed. * @@ -79,14 +79,14 @@ #include /* - * Safely assign a pointer. + * Safely store a pointer. */ -#define llsync_assign_ptr(ptr, value) atomic_store(&(ptr), value, ATOMIC_RELEASE) +#define llsync_store_ptr(ptr, value) atomic_store(&(ptr), value, ATOMIC_RELEASE) /* - * Safely access a pointer. + * Safely load a pointer. */ -#define llsync_read_ptr(ptr) atomic_load(&(ptr), ATOMIC_CONSUME) +#define llsync_load_ptr(ptr) atomic_load(&(ptr), ATOMIC_CONSUME) /* * Read-side critical section enter/exit functions. diff --git a/kern/rdxtree.c b/kern/rdxtree.c index 77005b6..678ec17 100644 --- a/kern/rdxtree.c +++ b/kern/rdxtree.c @@ -238,7 +238,7 @@ rdxtree_node_insert(struct rdxtree_node *node, unsigned short index, assert(node->entries[index] == NULL); node->nr_entries++; - llsync_assign_ptr(node->entries[index], entry); + llsync_store_ptr(node->entries[index], entry); } static inline void @@ -255,7 +255,7 @@ rdxtree_node_remove(struct rdxtree_node *node, unsigned short index) assert(node->entries[index] != NULL); node->nr_entries--; - llsync_assign_ptr(node->entries[index], NULL); + llsync_store_ptr(node->entries[index], NULL); } static inline void * @@ -267,7 +267,7 @@ rdxtree_node_find(struct rdxtree_node *node, unsigned short *indexp) index = *indexp; while (index < ARRAY_SIZE(node->entries)) { - ptr = rdxtree_entry_addr(llsync_read_ptr(node->entries[index])); + ptr = rdxtree_entry_addr(llsync_load_ptr(node->entries[index])); if (ptr != NULL) { *indexp = index; @@ -355,7 +355,7 @@ rdxtree_shrink(struct rdxtree *tree) rdxtree_node_unlink(rdxtree_entry_addr(entry)); } - llsync_assign_ptr(tree->root, entry); + llsync_store_ptr(tree->root, entry); /* * There is still one valid entry (the first one) in this node. It @@ -410,7 +410,7 @@ rdxtree_grow(struct rdxtree *tree, rdxtree_key_t key) rdxtree_node_insert(node, 0, tree->root); tree->height++; - llsync_assign_ptr(tree->root, rdxtree_node_to_entry(node)); + llsync_store_ptr(tree->root, rdxtree_node_to_entry(node)); root = node; } while (new_height > tree->height); @@ -433,7 +433,7 @@ rdxtree_cleanup(struct rdxtree *tree, struct rdxtree_node *node) if (node->parent == NULL) { tree->height = 0; - llsync_assign_ptr(tree->root, NULL); + llsync_store_ptr(tree->root, NULL); rdxtree_node_schedule_destruction(node); break; } @@ -488,7 +488,7 @@ rdxtree_insert_common(struct rdxtree *tree, rdxtree_key_t key, return ERROR_BUSY; } - llsync_assign_ptr(tree->root, ptr); + llsync_store_ptr(tree->root, ptr); if (slotp != NULL) { *slotp = &tree->root; @@ -516,7 +516,7 @@ rdxtree_insert_common(struct rdxtree *tree, rdxtree_key_t key, } if (prev == NULL) { - llsync_assign_ptr(tree->root, rdxtree_node_to_entry(node)); + llsync_store_ptr(tree->root, rdxtree_node_to_entry(node)); } else { rdxtree_node_link(node, prev, index); rdxtree_node_insert_node(prev, index, node); @@ -565,7 +565,7 @@ rdxtree_insert_alloc_common(struct rdxtree *tree, void *ptr, if (unlikely(height == 0)) { if (tree->root == NULL) { - llsync_assign_ptr(tree->root, ptr); + llsync_store_ptr(tree->root, ptr); *keyp = 0; if (slotp != NULL) { @@ -661,7 +661,7 @@ rdxtree_remove(struct rdxtree *tree, rdxtree_key_t key) node = rdxtree_entry_addr(tree->root); if (unlikely(height == 0)) { - llsync_assign_ptr(tree->root, NULL); + llsync_store_ptr(tree->root, NULL); return node; } @@ -700,7 +700,7 @@ rdxtree_lookup_common(const struct rdxtree *tree, rdxtree_key_t key, unsigned short height, shift, index; void *entry; - entry = llsync_read_ptr(tree->root); + entry = llsync_load_ptr(tree->root); if (entry == NULL) { node = NULL; @@ -731,7 +731,7 @@ rdxtree_lookup_common(const struct rdxtree *tree, rdxtree_key_t key, prev = node; index = (unsigned short)(key >> shift) & RDXTREE_RADIX_MASK; - entry = llsync_read_ptr(node->entries[index]); + entry = llsync_load_ptr(node->entries[index]); node = rdxtree_entry_addr(entry); shift -= RDXTREE_RADIX; height--; @@ -755,7 +755,7 @@ rdxtree_replace_slot(void **slot, void *ptr) old = *slot; assert(old != NULL); rdxtree_assert_alignment(old); - llsync_assign_ptr(*slot, ptr); + llsync_store_ptr(*slot, ptr); return old; } @@ -767,7 +767,7 @@ rdxtree_walk_next(struct rdxtree *tree, struct rdxtree_iter *iter) rdxtree_key_t key; void *entry; - entry = llsync_read_ptr(tree->root); + entry = llsync_load_ptr(tree->root); if (entry == NULL) { return NULL; @@ -863,7 +863,7 @@ rdxtree_remove_all(struct rdxtree *tree) if (tree->height == 0) { if (tree->root != NULL) { - llsync_assign_ptr(tree->root, NULL); + llsync_store_ptr(tree->root, NULL); } return; diff --git a/kern/rdxtree.h b/kern/rdxtree.h index 6ae1e6e..1430208 100644 --- a/kern/rdxtree.h +++ b/kern/rdxtree.h @@ -160,7 +160,7 @@ rdxtree_lookup_slot(const struct rdxtree *tree, rdxtree_key_t key) static inline void * rdxtree_load_slot(void **slot) { - return llsync_read_ptr(*slot); + return llsync_load_ptr(*slot); } /* diff --git a/kern/slist.h b/kern/slist.h index 8d42db2..0e26c05 100644 --- a/kern/slist.h +++ b/kern/slist.h @@ -301,7 +301,7 @@ for (entry = slist_first_entry(list, typeof(*entry), member), \ static inline struct slist_node * slist_llsync_first(const struct slist *list) { - return llsync_read_ptr(list->first); + return llsync_load_ptr(list->first); } /* @@ -310,7 +310,7 @@ slist_llsync_first(const struct slist *list) static inline struct slist_node * slist_llsync_next(const struct slist_node *node) { - return llsync_read_ptr(node->next); + return llsync_load_ptr(node->next); } /* @@ -324,7 +324,7 @@ slist_llsync_insert_head(struct slist *list, struct slist_node *node) } node->next = list->first; - llsync_assign_ptr(list->first, node); + llsync_store_ptr(list->first, node); } /* @@ -336,9 +336,9 @@ slist_llsync_insert_tail(struct slist *list, struct slist_node *node) node->next = NULL; if (slist_empty(list)) { - llsync_assign_ptr(list->first, node); + llsync_store_ptr(list->first, node); } else { - llsync_assign_ptr(list->last->next, node); + llsync_store_ptr(list->last->next, node); } list->last = node; @@ -356,7 +356,7 @@ slist_llsync_insert_after(struct slist *list, struct slist_node *prev, struct slist_node *node) { node->next = prev->next; - llsync_assign_ptr(prev->next, node); + llsync_store_ptr(prev->next, node); if (list->last == prev) { list->last = node; @@ -377,14 +377,14 @@ slist_llsync_remove(struct slist *list, struct slist_node *prev) if (slist_end(prev)) { node = list->first; - llsync_assign_ptr(list->first, node->next); + llsync_store_ptr(list->first, node->next); if (list->last == node) { list->last = NULL; } } else { node = prev->next; - llsync_assign_ptr(prev->next, node->next); + llsync_store_ptr(prev->next, node->next); if (list->last == node) { list->last = prev; @@ -397,7 +397,7 @@ slist_llsync_remove(struct slist *list, struct slist_node *prev) * given node based on the given type and member. */ #define slist_llsync_entry(node, type, member) \ - structof(llsync_read_ptr(node), type, member) + structof(llsync_load_ptr(node), type, member) /* * Get the first entry of a list. diff --git a/test/test_llsync_defer.c b/test/test_llsync_defer.c index 4b8afbc..c87aff7 100644 --- a/test/test_llsync_defer.c +++ b/test/test_llsync_defer.c @@ -87,7 +87,7 @@ test_alloc(void *arg) } } - llsync_assign_ptr(test_pdsc, pdsc); + llsync_store_ptr(test_pdsc, pdsc); condition_signal(&test_condition); if ((i % TEST_LOOPS_PER_PRINT) == 0) { @@ -130,7 +130,7 @@ test_free(void *arg) } pdsc = test_pdsc; - llsync_assign_ptr(test_pdsc, NULL); + llsync_store_ptr(test_pdsc, NULL); if (pdsc != NULL) { work_init(&pdsc->work, test_deferred_free); @@ -161,7 +161,7 @@ test_read(void *arg) for (;;) { llsync_read_enter(); - pdsc = llsync_read_ptr(test_pdsc); + pdsc = llsync_load_ptr(test_pdsc); if (pdsc != NULL) { s = (const unsigned char *)pdsc->addr; -- cgit v1.2.3 From b8d62ebb97834f615260822553aa3ab7da0ea489 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 21:10:47 +0200 Subject: kern/slist: fix comment --- kern/slist.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kern/slist.h b/kern/slist.h index 0e26c05..dd6a2d7 100644 --- a/kern/slist.h +++ b/kern/slist.h @@ -347,9 +347,7 @@ slist_llsync_insert_tail(struct slist *list, struct slist_node *node) /* * Insert a node after another node. * - * The prev argument is used to determine the insertion point. It may safely - * denote the end of the given list, in which case the node is inserted at - * the head of the list. + * The prev node must be valid. */ static inline void slist_llsync_insert_after(struct slist *list, struct slist_node *prev, -- cgit v1.2.3 From 35a39b5b7e89870daea062d3fe433b15c7697827 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 21:18:42 +0200 Subject: kern/sref: improve sref_weakref_tryget --- kern/sref.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kern/sref.c b/kern/sref.c index 9c49a42..5cb6620 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -288,11 +288,10 @@ sref_weakref_tryget(struct sref_weakref *weakref) { uintptr_t addr, oldval, newval; - /* TODO Review */ do { addr = atomic_load(&weakref->addr, ATOMIC_RELAXED); newval = addr & SREF_WEAKREF_MASK; - oldval = atomic_cas_acquire(&weakref->addr, addr, newval); + oldval = atomic_cas(&weakref->addr, addr, newval, ATOMIC_RELAXED); } while (oldval != addr); return (struct sref_counter *)newval; -- cgit v1.2.3 From cfcd4d2d503c0f34cb975ca5c8d130af4039121d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 21:26:57 +0200 Subject: kern/rtmutex: improve variable naming Use the same names as in the mutex_adaptive module. --- kern/rtmutex.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kern/rtmutex.c b/kern/rtmutex.c index fcf5f35..db23920 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -36,11 +36,11 @@ void rtmutex_lock_slow(struct rtmutex *rtmutex) { struct turnstile *turnstile; - uintptr_t owner, prev_owner; /* TODO Review names */ + uintptr_t self, owner; struct thread *thread; uintptr_t bits; - owner = (uintptr_t)thread_self(); + self = (uintptr_t)thread_self(); turnstile = turnstile_lend(rtmutex); @@ -49,14 +49,14 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) bits = RTMUTEX_CONTENDED; for (;;) { - prev_owner = atomic_cas_acquire(&rtmutex->owner, bits, owner | bits); - assert((prev_owner & bits) == bits); + owner = atomic_cas_acquire(&rtmutex->owner, bits, self | bits); + assert((owner & bits) == bits); - if (prev_owner == bits) { + if (owner == bits) { break; } - thread = (struct thread *)(prev_owner & RTMUTEX_OWNER_MASK); + thread = (struct thread *)(owner & RTMUTEX_OWNER_MASK); turnstile_wait(turnstile, "rtmutex", thread); bits |= RTMUTEX_FORCE_WAIT; } @@ -64,8 +64,8 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) turnstile_own(turnstile); if (turnstile_empty(turnstile)) { - prev_owner = atomic_swap(&rtmutex->owner, owner, ATOMIC_RELAXED); - assert(prev_owner == (owner | bits)); + owner = atomic_swap(&rtmutex->owner, self, ATOMIC_RELAXED); + assert(owner == (self | bits)); } turnstile_return(turnstile); @@ -82,16 +82,16 @@ void rtmutex_unlock_slow(struct rtmutex *rtmutex) { struct turnstile *turnstile; - uintptr_t owner, prev_owner; + uintptr_t self, owner; - owner = (uintptr_t)thread_self(); + self = (uintptr_t)thread_self(); turnstile = turnstile_acquire(rtmutex); assert(turnstile != NULL); - prev_owner = atomic_swap_release(&rtmutex->owner, - RTMUTEX_FORCE_WAIT | RTMUTEX_CONTENDED); - assert((prev_owner & RTMUTEX_OWNER_MASK) == owner); + owner = atomic_swap_release(&rtmutex->owner, + RTMUTEX_FORCE_WAIT | RTMUTEX_CONTENDED); + assert((owner & RTMUTEX_OWNER_MASK) == self); turnstile_disown(turnstile); turnstile_signal(turnstile); -- cgit v1.2.3 From 0fb916cd84b40c3b3df7b01788ae03ed52fc03cb Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 25 Jul 2017 21:43:07 +0200 Subject: doc/style(9): discuss variable shadowing in function-like macros --- doc/style.9.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/style.9.txt b/doc/style.9.txt index 7209567..30322f3 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -651,6 +651,17 @@ expressions, there is no difference between prefix and postfix operators. The latter are preferred because they look more object-oriented, somewhat matching the object.method() syntax. +Function-like macros with local variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Such macros may cause variable shadowing if one of their local variables has +the same name as one of the arguments. To avoid such situations, local +variables in function-like macros should be named with a prefix of underscores. +The C specification describes the use of single and double underscore prefixes +for file-scope specifiers and names reserved for the implementation. It's +therefore recommended to use three underscores when naming the local variables +of function-like macros. + SEE --- -- cgit v1.2.3 From 22b1aecea7a3d1e8f5388e8f8bd69046a4c7ff88 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 26 Jul 2017 01:19:01 +0200 Subject: tools/tsort_init_ops.sh: new script This script retrieves all initialization operation dependencies, taking all build options into account, and attempts a topological sort, failing in case loops are detected. It's automatically run by the build system as a dependency of the kernel binary. --- .gitignore | 1 + Makefile.am | 7 +++++-- Makefrag.am | 4 +++- tools/tsort_init_ops.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100755 tools/tsort_init_ops.sh diff --git a/.gitignore b/.gitignore index 452070d..265e3aa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ x15.lds /configure /stamp-h1 /x15 +/x15.sorted_init_ops /doc/*\.[1-9] /doc/*\.[1-9]\.html diff --git a/Makefile.am b/Makefile.am index 50baae9..413ac6d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,10 +57,13 @@ SUFFIXES += .9 .9.html .9.xml .9.txt $(AM_V_GEN)$(A2X) $(A2X_FLAGS) -f manpage --xsltproc-opts="-o $(abs_top_builddir)/$@" $< bin_PROGRAMS = x15 -x15_DEPENDENCIES = arch/$(arch)/x15.lds -MOSTLYCLEANFILES += arch/$(arch)/x15.lds +x15_DEPENDENCIES = arch/$(arch)/x15.lds x15.sorted_init_ops +MOSTLYCLEANFILES += $(x15_DEPENDENCIES) x15_SOURCES = x15_LDFLAGS = -nostdlib -Xlinker -T arch/$(arch)/x15.lds x15_LDADD = -lgcc include Makefrag.am + +x15.sorted_init_ops: $(filter %.c,$(x15_SOURCES)) + $(AM_V_GEN)$(top_srcdir)/tools/tsort_init_ops.sh "$(COMPILE)" "$@" $^ diff --git a/Makefrag.am b/Makefrag.am index 8c2e35e..1b39b72 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -1,7 +1,9 @@ include arch/x86/Makefrag.am include doc/Makefrag.am -EXTRA_DIST += tools/qemu.sh +EXTRA_DIST += \ + tools/qemu.sh \ + tools/tsort_init_ops.sh x15_SOURCES += \ include/assert.h \ diff --git a/tools/tsort_init_ops.sh b/tools/tsort_init_ops.sh new file mode 100755 index 0000000..a367ef2 --- /dev/null +++ b/tools/tsort_init_ops.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +set -e + +compile="$1" +output_file="$2" + +shift 2 + +tmpfile1=$(mktemp) +tmpfile2=$(mktemp) + +cleanup() +{ + rm -f $tmpfile1 $tmpfile2 +} + +trap cleanup EXIT + +process() +{ + target=$1 + + shift + + for dep in $@; do + echo $target $dep + done +} + +# Define _KERN_INIT_H so that the INIT_XXX macros aren't expanded. +$compile -E -D_KERN_INIT_H "$@" \ + | sed -e 's/#.*$//' \ + | tr '\n' ' ' \ + | tr -s ' ' \ + | sed -E -e 's/INIT_OP_DEP\(([a-zA-Z0-9_]*), 1 \)/\1/g' \ + | grep -P -o 'INIT_OP_DEFINE\(.*?\)' \ + | sed -e 's/^INIT_OP_DEFINE(//' \ + | sed -e 's/).*$//' \ + | tr -d , \ +| while read line; do + process $line +done > $tmpfile1 + +if [ -z "$(cat $tmpfile1)" ]; then + return 1 +fi + +# XXX Avoid using pipes because of the lack of a standard -o pipefail variant. +tsort < $tmpfile1 > $tmpfile2 +tac < $tmpfile2 > "$output_file" -- cgit v1.2.3 From af496bdc3c4c064a376cf44330e23ec340cac144 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Jul 2017 00:16:40 +0200 Subject: doc/init(9): new man page --- doc/Makefrag.am | 1 + doc/asciidoc.conf | 27 +++++++++++++++++++ doc/init.9.txt | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/intro.9.txt | 2 ++ 4 files changed, 107 insertions(+) create mode 100644 doc/init.9.txt diff --git a/doc/Makefrag.am b/doc/Makefrag.am index 0869fdf..86c468c 100644 --- a/doc/Makefrag.am +++ b/doc/Makefrag.am @@ -1,6 +1,7 @@ EXTRA_DIST += doc/asciidoc.conf x15_DOCS = \ + doc/init.9.txt \ doc/intro.9.txt \ doc/style.9.txt diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf index ee2bb5f..a413768 100644 --- a/doc/asciidoc.conf +++ b/doc/asciidoc.conf @@ -9,6 +9,9 @@ the-art-of-unix-programming=http://www.catb.org/esr/writings/taoup/html/[The Art (?su)(?Pmodule_mutex):(?P\w+)= (?su)(?Pmodule):(?P\w+)/(?P\w+)= (?su)(?Pmanpage):(?P\w+)= +(?su)(?Pcfunction):(?P\w+)= +(?su)(?Pcmacro):(?P\w+)= +(?su)(?Pshell):{(?P[^}]+)}= [module_mutex-inlinemacro] ifdef::basebackend-html[] @@ -33,3 +36,27 @@ endif::basebackend-html[] ifdef::basebackend-docbook[] {page}(9) endif::basebackend-docbook[] + +[cfunction-inlinemacro] +ifdef::basebackend-html[] +{function}() +endif::basebackend-html[] +ifdef::basebackend-docbook[] +{function}() +endif::basebackend-docbook[] + +[cmacro-inlinemacro] +ifdef::basebackend-html[] +{macro} +endif::basebackend-html[] +ifdef::basebackend-docbook[] +{macro} +endif::basebackend-docbook[] + +[shell-inlinemacro] +ifdef::basebackend-html[] +{command} +endif::basebackend-html[] +ifdef::basebackend-docbook[] +{command} +endif::basebackend-docbook[] diff --git a/doc/init.9.txt b/doc/init.9.txt new file mode 100644 index 0000000..f1aa138 --- /dev/null +++ b/doc/init.9.txt @@ -0,0 +1,77 @@ +INIT(9) +======== +:doctype: manpage +:man source: X15 +:man manual: X15 Kernel initialization + +NAME +---- + +init - kernel initialization + +DESCRIPTION +----------- + +This document describes kernel initialization, and in particular, the concept +of initialization operation and their dependencies. All related declarations +are provided by the module:kern/init module. + +Initialization operations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +An initialization operation is a function that enables the use of a specific +interface. For example, The cfunction:thread_setup function enables the +creation of threads. Any thread created before calling that function is +invalid and leads to undefined behaviour, potentially being silent and +unnoticed for a long time. This kind of relation is expressed as an +initialization operation dependency. As a result, the data structure for +initialization operations associates a function and its dependencies. + +Definition +~~~~~~~~~~ + +Initialization operations are defined using the cmacro:INIT_OP_DEFINE() +macro. The first argument is the function implementing the operation, +and any additional argument is a dependency. + +Dependencies are built with the cmacro:INIT_OP_DEP() macro. They may +be either required or optional. An optional dependency is only used +to infer execution order. If it fails, the initialization operation +function is run regardless. + +Declaration +~~~~~~~~~~~ + +In order to build dependencies, initialization operations must be +declared as part of the public interface of the module providing it. +This is accomplished using the cmacro:INIT_OP_DECLARE() macro in the +public header. + +Execution order +~~~~~~~~~~~~~~~ + +The complete set of initialization operations and their dependencies +must result in a directed acyclic graph, so that a topological sort +can be applied, starting from the roots, i.e. initialization operations +with no dependencies. For convenience, the topological sort is performed +both at build and run time. Sorting at build time is done with the +shell:{make x15.sorted_init_ops} command. + +Debugging +~~~~~~~~~ + +In order to help debugging initialization issues, the module:kern/init +module provides a debugging mode, that may be enabled at build time +by changing an internal macro. In this mode, text buffers record +various information such as the list of roots, operations with dependency +cycles (in case the build time check isn't used), and the list of +operations that completed. These buffers can be dumped using a debugger +such as a JTAG interface. Debugging mode is enabled by setting the +cmacro:INIT_DEBUG macro to a non-zero value. + +SEE +--- + +manpage:intro + +{x15-operating-system} diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 515edbf..5af5a4e 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -276,4 +276,6 @@ SEE manpage:style +manpage:init + {x15-operating-system} -- cgit v1.2.3 From bbc4b610d50c529152db9abe1884628d9b05cdd3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Jul 2017 00:18:09 +0200 Subject: tools/tsort_init_ops.sh: mention that the script is part of the build system --- tools/tsort_init_ops.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/tsort_init_ops.sh b/tools/tsort_init_ops.sh index a367ef2..a9c6e89 100755 --- a/tools/tsort_init_ops.sh +++ b/tools/tsort_init_ops.sh @@ -1,5 +1,7 @@ #!/bin/sh +# This script is meant to be run by the build system. + set -e compile="$1" -- cgit v1.2.3 From 7dad3346fcbe327eb14aa2902e8cbc8bbe40b05d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 29 Jul 2017 11:12:47 +0200 Subject: x86/boot: add a todo entry about EFI support --- arch/x86/machine/boot.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 25e92ff..82b5330 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -40,6 +40,8 @@ * almost everywhere, the latter solution is implemented (a small part of * 32-bit code is required until the identity mapping is in place). Mentions * to "enabling paging" do not refer to this initial identity mapping. + * + * TODO EFI support. */ #include -- cgit v1.2.3 From b9411198e3853793b4f9cf10af65587875f5f478 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 30 Jul 2017 17:58:09 +0200 Subject: kern/thread: replace the reaper with deferred works --- kern/thread.c | 83 ++++++++++------------------------------------------------- 1 file changed, 13 insertions(+), 70 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 8e5b2b5..29173d9 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -339,15 +339,8 @@ static unsigned int thread_nr_keys __read_mostly; */ static thread_dtor_fn_t thread_dtors[THREAD_KEYS_MAX] __read_mostly; -/* - * List of threads pending for destruction by the reaper. - */ -static struct mutex thread_reap_lock; -static struct condition thread_reap_cond; -static struct list thread_reap_list; - struct thread_zombie { - struct list node; + struct work work; struct thread *thread; }; @@ -2001,55 +1994,6 @@ thread_join_common(struct thread *thread) thread_unref(thread); } -static void -thread_reap(void *arg) -{ - struct thread_zombie *zombie; - struct list zombies; - - (void)arg; - - for (;;) { - mutex_lock(&thread_reap_lock); - - while (list_empty(&thread_reap_list)) { - condition_wait(&thread_reap_cond, &thread_reap_lock); - } - - list_set_head(&zombies, &thread_reap_list); - list_init(&thread_reap_list); - - mutex_unlock(&thread_reap_lock); - - while (!list_empty(&zombies)) { - zombie = list_first_entry(&zombies, struct thread_zombie, node); - list_remove(&zombie->node); - thread_join_common(zombie->thread); - } - } - - /* Never reached */ -} - -static void __init -thread_setup_reaper(void) -{ - struct thread_attr attr; - struct thread *thread; - int error; - - mutex_init(&thread_reap_lock); - condition_init(&thread_reap_cond); - list_init(&thread_reap_list); - - thread_attr_init(&attr, THREAD_KERNEL_PREFIX "thread_reap"); - error = thread_create(&thread, &attr, thread_reap, NULL); - - if (error) { - panic("thread: unable to create reaper thread"); - } -} - static void thread_balance_idle_tick(struct thread_runq *runq) { @@ -2333,8 +2277,6 @@ thread_setup(void) CPU_DATA_ALIGN, NULL, 0); #endif /* X15_THREAD_STACK_GUARD */ - thread_setup_reaper(); - cpumap_for_each(&thread_active_runqs, cpu) { thread_setup_runq(percpu_ptr(thread_runq, cpu)); } @@ -2418,6 +2360,15 @@ error_thread: return error; } +static void +thread_reap(struct work *work) +{ + struct thread_zombie *zombie; + + zombie = structof(work, struct thread_zombie, work); + thread_join_common(zombie->thread); +} + void thread_exit(void) { @@ -2431,25 +2382,17 @@ thread_exit(void) if (thread_test_flag(thread, THREAD_DETACHED)) { zombie.thread = thread; - mutex_lock(&thread_reap_lock); - list_insert_tail(&thread_reap_list, &zombie.node); - condition_signal(&thread_reap_cond); - mutex_unlock(&thread_reap_lock); + work_init(&zombie.work, thread_reap); + work_schedule(&zombie.work, 0); } mutex_lock(&thread->join_lock); thread->exited = 1; condition_signal(&thread->join_cond); - /* - * Disable preemption before releasing the mutex to make sure the current - * thread becomes dead as soon as possible. This is important because the - * joining thread actively polls the thread state before destroying it. - */ - thread_preempt_disable(); - mutex_unlock(&thread->join_lock); + thread_preempt_disable(); runq = thread_runq_local(); spinlock_lock_intr_save(&runq->lock, &flags); -- cgit v1.2.3 From 9bb3b8ca7f4a6d9ff33244a98673b3ffe762d6c2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 30 Jul 2017 18:35:34 +0200 Subject: doc/style(9): relax boolean coercion rules even more --- doc/style.9.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index 30322f3..c61bc77 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -613,7 +613,9 @@ the macro used to declare an array again in the iteration code. Boolean Coercion ~~~~~~~~~~~~~~~~ -Boolean coercion is forbidden by default. It is only allowed when +Boolean coercion should only be used when the resulting text is semantically +valid, i.e. it can be understood that the expression is either true, or false. + checking error codes and pointers. For example : [source,c] @@ -622,6 +624,7 @@ int error; error = do_something(); +/* Valid */ if (error) { if (error != ERROR_AGAIN) { printf("unexpected error\n"); @@ -629,6 +632,18 @@ if (error) { return error; } + +int color; + +color = get_color(); + +/* Invalid */ +if (!color) { + print_black(); +} else if (color == 1) { + print_red(); +} + -------------------------------------------------------------------------------- There are historical uses of the *int* type for boolean values, but all of -- cgit v1.2.3 From e672836cb003dc1ae94fc3cfc489764675b52111 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 31 Jul 2017 20:36:49 +0200 Subject: kern/{sleepq,turnstile}: remove unused initialization operation --- kern/sleepq.c | 13 ++----------- kern/sleepq.h | 6 ------ kern/turnstile.c | 13 ++----------- kern/turnstile.h | 6 ------ 4 files changed, 4 insertions(+), 34 deletions(-) diff --git a/kern/sleepq.c b/kern/sleepq.c index 44ad996..99282d9 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -194,7 +194,7 @@ sleepq_ctor(void *ptr) } static int __init -sleepq_bootstrap(void) +sleepq_setup(void) { unsigned int i; @@ -206,22 +206,13 @@ sleepq_bootstrap(void) sleepq_bucket_init(&sleepq_cond_htable[i]); } - return 0; -} - -INIT_OP_DEFINE(sleepq_bootstrap); - -static int __init -sleepq_setup(void) -{ kmem_cache_init(&sleepq_cache, "sleepq", sizeof(struct sleepq), CPU_L1_SIZE, sleepq_ctor, 0); return 0; } INIT_OP_DEFINE(sleepq_setup, - INIT_OP_DEP(kmem_setup, true), - INIT_OP_DEP(sleepq_bootstrap, true)); + INIT_OP_DEP(kmem_setup, true)); struct sleepq * sleepq_create(void) diff --git a/kern/sleepq.h b/kern/sleepq.h index 651b3e7..3de765a 100644 --- a/kern/sleepq.h +++ b/kern/sleepq.h @@ -130,12 +130,6 @@ void sleepq_wait(struct sleepq *sleepq, const char *wchan); */ void sleepq_signal(struct sleepq *sleepq); -/* - * This init operation provides : - * - ? TODO Review - */ -INIT_OP_DECLARE(sleepq_bootstrap); - /* * This init operation provides : * - sleepq creation diff --git a/kern/turnstile.c b/kern/turnstile.c index e724c1e..19eeeaf 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -501,7 +501,7 @@ turnstile_ctor(void *ptr) } static int __init -turnstile_bootstrap(void) +turnstile_setup(void) { unsigned int i; @@ -509,22 +509,13 @@ turnstile_bootstrap(void) turnstile_bucket_init(&turnstile_htable[i]); } - return 0; -} - -INIT_OP_DEFINE(turnstile_bootstrap); - -static int __init -turnstile_setup(void) -{ kmem_cache_init(&turnstile_cache, "turnstile", sizeof(struct turnstile), CPU_L1_SIZE, turnstile_ctor, 0); return 0; } INIT_OP_DEFINE(turnstile_setup, - INIT_OP_DEP(kmem_setup, true), - INIT_OP_DEP(turnstile_bootstrap, true)); + INIT_OP_DEP(kmem_setup, true)); struct turnstile * turnstile_create(void) diff --git a/kern/turnstile.h b/kern/turnstile.h index 74512fa..0473a4c 100644 --- a/kern/turnstile.h +++ b/kern/turnstile.h @@ -184,12 +184,6 @@ void turnstile_signal(struct turnstile *turnstile); void turnstile_own(struct turnstile *turnstile); void turnstile_disown(struct turnstile *turnstile); -/* - * This init operation provides : - * - ? TODO Review - */ -INIT_OP_DECLARE(turnstile_bootstrap); - /* * This init operation provides : * - turnstile creation -- cgit v1.2.3 From f2a072f2a8ebaca76fa3f5b5fdb828b33c93e3e0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 4 Aug 2017 09:39:56 +0200 Subject: doc/style(9): forbid declaring arguments as arrays --- doc/style.9.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/style.9.txt b/doc/style.9.txt index c61bc77..e479c8c 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -548,6 +548,18 @@ with link-time optimizations (LTO), public functions can also be inlined or even removed if unused. As a result, use inline for a few performance critical short functions only. +Passing arrays as arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The C language automatically converts arrays to pointers when passed as +function arguments. As a result, declaring an argument as an array is +misleading. In particular, it may give the impression that the sizeof +operator may be used safely to get the size of the array, when it +actually always returns the size of a pointer. Therefore, declaring +arguments as arrays is strictly forbidden. + +TODO Fix historic array argument declarations. + GIT COMMITS ----------- -- cgit v1.2.3 From cd89c58afd1917348157d5fceaa3657765c61ac3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 4 Aug 2017 09:50:26 +0200 Subject: vm/vm_page: rename vm_page_atop and vm_page_ptoa Talk about "bytes" instead of "addresses" for better clarity. --- arch/x86/machine/biosmem.c | 2 +- vm/vm_kmem.c | 2 +- vm/vm_object.c | 6 +++--- vm/vm_page.c | 24 ++++++++++++------------ vm/vm_page.h | 18 ++++++++---------- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/arch/x86/machine/biosmem.c b/arch/x86/machine/biosmem.c index fc03614..370c561 100644 --- a/arch/x86/machine/biosmem.c +++ b/arch/x86/machine/biosmem.c @@ -712,7 +712,7 @@ biosmem_bootalloc(unsigned int nr_pages) { unsigned long addr, size; - size = vm_page_ptoa(nr_pages); + size = vm_page_ptob(nr_pages); if (size == 0) { boot_panic(biosmem_panic_inval_msg); diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index dc204f8..101c981 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -150,7 +150,7 @@ vm_kmem_alloc(size_t size) error = pmap_enter(kernel_pmap, start, vm_page_to_pa(page), VM_PROT_READ | VM_PROT_WRITE, PMAP_PEF_GLOBAL); - if (error || (start - va == vm_page_ptoa(1000))) { + if (error || (start - va == vm_page_ptob(1000))) { goto error; } } diff --git a/vm/vm_object.c b/vm/vm_object.c index 649d857..679f353 100644 --- a/vm/vm_object.c +++ b/vm/vm_object.c @@ -74,7 +74,7 @@ vm_object_insert(struct vm_object *object, struct vm_page *page, goto error; } - error = rdxtree_insert(&object->pages, vm_page_atop(offset), page); + error = rdxtree_insert(&object->pages, vm_page_btop(offset), page); if (error) { goto error; @@ -109,7 +109,7 @@ vm_object_remove(struct vm_object *object, uint64_t start, uint64_t end) mutex_lock(&object->lock); for (offset = start; offset < end; offset += PAGE_SIZE) { - page = rdxtree_remove(&object->pages, vm_page_atop(offset)); + page = rdxtree_remove(&object->pages, vm_page_btop(offset)); if (page == NULL) { continue; @@ -133,7 +133,7 @@ vm_object_lookup(struct vm_object *object, uint64_t offset) llsync_read_enter(); do { - page = rdxtree_lookup(&object->pages, vm_page_atop(offset)); + page = rdxtree_lookup(&object->pages, vm_page_btop(offset)); if (page == NULL) { break; diff --git a/vm/vm_page.c b/vm/vm_page.c index 7b0eb3e..49c2c11 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -266,13 +266,13 @@ vm_page_zone_free_to_buddy(struct vm_page_zone *zone, struct vm_page *page, pa = page->phys_addr; while (order < (VM_PAGE_NR_FREE_LISTS - 1)) { - buddy_pa = pa ^ vm_page_ptoa(1 << order); + buddy_pa = pa ^ vm_page_ptob(1 << order); if ((buddy_pa < zone->start) || (buddy_pa >= zone->end)) { break; } - buddy = &zone->pages[vm_page_atop(buddy_pa - zone->start)]; + buddy = &zone->pages[vm_page_btop(buddy_pa - zone->start)]; if (buddy->order != order) { break; @@ -281,8 +281,8 @@ vm_page_zone_free_to_buddy(struct vm_page_zone *zone, struct vm_page *page, vm_page_free_list_remove(&zone->free_lists[order], buddy); buddy->order = VM_PAGE_ORDER_UNLISTED; order++; - pa &= -vm_page_ptoa(1 << order); - page = &zone->pages[vm_page_atop(pa - zone->start)]; + pa &= -vm_page_ptob(1 << order); + page = &zone->pages[vm_page_btop(pa - zone->start)]; } vm_page_free_list_insert(&zone->free_lists[order], page); @@ -383,7 +383,7 @@ vm_page_zone_compute_pool_size(struct vm_page_zone *zone) { phys_addr_t size; - size = vm_page_atop(vm_page_zone_size(zone)) / VM_PAGE_CPU_POOL_RATIO; + size = vm_page_btop(vm_page_zone_size(zone)) / VM_PAGE_CPU_POOL_RATIO; if (size == 0) { size = 1; @@ -411,7 +411,7 @@ vm_page_zone_init(struct vm_page_zone *zone, phys_addr_t start, phys_addr_t end, } zone->pages = pages; - zone->pages_end = pages + vm_page_atop(vm_page_zone_size(zone)); + zone->pages_end = pages + vm_page_btop(vm_page_zone_size(zone)); mutex_init(&zone->lock); for (i = 0; i < ARRAY_SIZE(zone->free_lists); i++) { @@ -422,7 +422,7 @@ vm_page_zone_init(struct vm_page_zone *zone, phys_addr_t start, phys_addr_t end, i = zone - vm_page_zones; for (pa = zone->start; pa < zone->end; pa += PAGE_SIZE) { - vm_page_init(&pages[vm_page_atop(pa - zone->start)], i, pa); + vm_page_init(&pages[vm_page_btop(pa - zone->start)], i, pa); } } @@ -712,7 +712,7 @@ vm_page_setup(void) nr_pages = 0; for (i = 0; i < vm_page_zones_size; i++) { - nr_pages += vm_page_atop(vm_page_boot_zone_size(&vm_page_boot_zones[i])); + nr_pages += vm_page_btop(vm_page_boot_zone_size(&vm_page_boot_zones[i])); } table_size = vm_page_round(nr_pages * sizeof(struct vm_page)); @@ -730,9 +730,9 @@ vm_page_setup(void) zone = &vm_page_zones[i]; boot_zone = &vm_page_boot_zones[i]; vm_page_zone_init(zone, boot_zone->start, boot_zone->end, table); - page = zone->pages + vm_page_atop(boot_zone->avail_start + page = zone->pages + vm_page_btop(boot_zone->avail_start - boot_zone->start); - end = zone->pages + vm_page_atop(boot_zone->avail_end + end = zone->pages + vm_page_btop(boot_zone->avail_end - boot_zone->start); while (page < end) { @@ -741,7 +741,7 @@ vm_page_setup(void) page++; } - table += vm_page_atop(vm_page_zone_size(zone)); + table += vm_page_btop(vm_page_zone_size(zone)); } while (va < (uintptr_t)table) { @@ -783,7 +783,7 @@ vm_page_lookup(phys_addr_t pa) zone = &vm_page_zones[i]; if ((pa >= zone->start) && (pa < zone->end)) { - return &zone->pages[vm_page_atop(pa - zone->start)]; + return &zone->pages[vm_page_btop(pa - zone->start)]; } } diff --git a/vm/vm_page.h b/vm/vm_page.h index 4ca3420..70a0091 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -41,18 +41,16 @@ #include /* - * Address/page conversion and rounding macros (not inline functions to + * Byte/page conversion and rounding macros (not inline functions to * be easily usable on both virtual and physical addresses, which may not * have the same type size). - * - * TODO Rename btop and ptob. */ -#define vm_page_atop(addr) ((addr) >> PAGE_SHIFT) -#define vm_page_ptoa(page) ((page) << PAGE_SHIFT) -#define vm_page_trunc(addr) P2ALIGN(addr, PAGE_SIZE) -#define vm_page_round(addr) P2ROUND(addr, PAGE_SIZE) -#define vm_page_end(addr) P2END(addr, PAGE_SIZE) -#define vm_page_aligned(addr) P2ALIGNED(addr, PAGE_SIZE) +#define vm_page_btop(bytes) ((bytes) >> PAGE_SHIFT) +#define vm_page_ptob(pages) ((pages) << PAGE_SHIFT) +#define vm_page_trunc(bytes) P2ALIGN(bytes, PAGE_SIZE) +#define vm_page_round(bytes) P2ROUND(bytes, PAGE_SIZE) +#define vm_page_end(bytes) P2END(bytes, PAGE_SIZE) +#define vm_page_aligned(bytes) P2ALIGNED(bytes, PAGE_SIZE) /* * Zone selectors. @@ -109,7 +107,7 @@ void vm_page_set_type(struct vm_page *page, unsigned int order, static inline unsigned int vm_page_order(size_t size) { - return iorder2(vm_page_atop(vm_page_round(size))); + return iorder2(vm_page_btop(vm_page_round(size))); } static inline phys_addr_t -- cgit v1.2.3 From f780ce9bcec081b9327820d44412c07bf967095c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 6 Aug 2017 11:45:48 +0200 Subject: Fix dependencies on percpu_setup --- kern/llsync.c | 1 - kern/percpu.h | 3 +-- kern/sref.c | 1 - kern/work.c | 1 - kern/xcall.c | 1 - 5 files changed, 1 insertion(+), 6 deletions(-) diff --git a/kern/llsync.c b/kern/llsync.c index c432fc6..b4d09b5 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -110,7 +110,6 @@ llsync_setup(void) INIT_OP_DEFINE(llsync_setup, INIT_OP_DEP(log_setup, true), INIT_OP_DEP(mutex_setup, true), - INIT_OP_DEP(percpu_setup, true), INIT_OP_DEP(spinlock_setup, true), INIT_OP_DEP(syscnt_setup, true), INIT_OP_DEP(thread_bootstrap, true), diff --git a/kern/percpu.h b/kern/percpu.h index 879767f..385930a 100644 --- a/kern/percpu.h +++ b/kern/percpu.h @@ -112,8 +112,7 @@ INIT_OP_DECLARE(percpu_bootstrap); /* * This init operation provides : - * - percpu section is copied to a kernel buffer subsequently used as - * the template for future percpu areas + * - new percpu areas can be created */ INIT_OP_DECLARE(percpu_setup); diff --git a/kern/sref.c b/kern/sref.c index 5cb6620..3603e78 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -886,7 +886,6 @@ INIT_OP_DEFINE(sref_setup, INIT_OP_DEP(log_setup, true), INIT_OP_DEP(mutex_setup, true), INIT_OP_DEP(panic_setup, true), - INIT_OP_DEP(percpu_setup, true), INIT_OP_DEP(sref_bootstrap, true), INIT_OP_DEP(syscnt_setup, true), INIT_OP_DEP(thread_setup, true)); diff --git a/kern/work.c b/kern/work.c index 9aa3f40..4647522 100644 --- a/kern/work.c +++ b/kern/work.c @@ -502,7 +502,6 @@ INIT_OP_DEFINE(work_setup, INIT_OP_DEP(kmem_setup, true), INIT_OP_DEP(log_setup, true), INIT_OP_DEP(panic_setup, true), - INIT_OP_DEP(percpu_setup, true), INIT_OP_DEP(spinlock_setup, true), INIT_OP_DEP(syscnt_setup, true), INIT_OP_DEP(thread_bootstrap, true)); diff --git a/kern/xcall.c b/kern/xcall.c index cccb937..44bd41f 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -118,7 +118,6 @@ xcall_setup(void) } INIT_OP_DEFINE(xcall_setup, - INIT_OP_DEP(percpu_setup, true), INIT_OP_DEP(thread_bootstrap, true), INIT_OP_DEP(spinlock_setup, true)); -- cgit v1.2.3 From 29db52610d938bc815c90ddad3afac4f9d8f61b0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 11 Aug 2017 00:40:18 +0200 Subject: x86/atomic: fix regression in atomic_load_64 --- arch/x86/machine/atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/machine/atomic.h b/arch/x86/machine/atomic.h index 9f130a5..de695b9 100644 --- a/arch/x86/machine/atomic.h +++ b/arch/x86/machine/atomic.h @@ -48,7 +48,7 @@ */ #define atomic_load_64(ptr, mo) \ MACRO_BEGIN \ - uint64_t ___ret; \ + uint64_t ___ret = 0; \ \ __atomic_compare_exchange_n((uint64_t *)(ptr), &___ret, 0, \ false, mo, __ATOMIC_RELAXED); \ -- cgit v1.2.3 From ebf946b7c850dfb7de1ca68f54458ff9c8a65acf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 20 Aug 2017 17:39:13 +0200 Subject: kern/turnstile: improve some function names --- kern/turnstile.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kern/turnstile.c b/kern/turnstile.c index 19eeeaf..9860084 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -202,7 +202,7 @@ turnstile_remove_waiter(struct turnstile *turnstile, } static void -turnstile_waiter_requeue(struct turnstile *turnstile, +turnstile_requeue_waiter(struct turnstile *turnstile, struct turnstile_waiter *waiter) { unsigned int global_priority; @@ -363,7 +363,7 @@ turnstile_td_propagate_priority_loop(struct turnstile_td *td) * This couldn't be done while changing the thread's priority * because of locking restrictions. Do it now. */ - turnstile_waiter_requeue(turnstile, waiter); + turnstile_requeue_waiter(turnstile, waiter); spinlock_unlock(&td->lock); thread_unref(thread); @@ -663,7 +663,7 @@ turnstile_empty(const struct turnstile *turnstile) } static void -turnstile_set_owner(struct turnstile *turnstile, struct thread *owner) +turnstile_update_owner(struct turnstile *turnstile, struct thread *owner) { struct turnstile_td *td; @@ -711,7 +711,7 @@ turnstile_wait(struct turnstile *turnstile, const char *wchan, } } else { /* This function temporarily unlocks the turnstile */ - turnstile_set_owner(turnstile, owner); + turnstile_update_owner(turnstile, owner); } for (;;) { -- cgit v1.2.3 From 9e0a5dc54d62ad7702ca041dcd7a4adf07d814fa Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 20 Aug 2017 17:40:07 +0200 Subject: kern/turnstile: fix priority propagation --- kern/turnstile.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kern/turnstile.c b/kern/turnstile.c index 9860084..1e66777 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -677,6 +677,8 @@ turnstile_update_owner(struct turnstile *turnstile, struct thread *owner) if (turnstile->owner == NULL) { turnstile_td_own(td, turnstile); + } else { + turnstile_td_reown(td, turnstile); } spinlock_unlock(&turnstile->bucket->lock); -- cgit v1.2.3 From 079e1f89354dd275d2a2096f3df5d0ddfd24bb12 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 21 Aug 2017 20:42:34 +0200 Subject: kern/thread: fix and improve termination --- kern/thread.c | 55 ++++++++++++++++++++++++++++++++----------------------- kern/thread_i.h | 20 +++++++++++++++----- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 29173d9..78c75ba 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -99,7 +99,6 @@ #include #include #include -#include #include #include #include @@ -1825,9 +1824,9 @@ thread_init(struct thread *thread, void *stack, thread_set_user_priority(thread, attr->priority); thread_reset_real_priority(thread); memset(thread->tsd, 0, sizeof(thread->tsd)); - mutex_init(&thread->join_lock); - condition_init(&thread->join_cond); - thread->exited = 0; + thread->join_waiter = NULL; + spinlock_init(&thread->join_lock); + thread->exiting = false; thread->task = task; thread->stack = stack; strlcpy(thread->name, attr->name, sizeof(thread->name)); @@ -1957,16 +1956,8 @@ thread_free_stack(void *stack) void thread_destroy(struct thread *thread) { - struct thread_runq *runq; - unsigned long flags, state; - assert(thread != thread_self()); - - do { - runq = thread_lock_runq(thread, &flags); - state = thread->state; - thread_unlock_runq(runq, flags); - } while (state != THREAD_DEAD); + assert(thread->state == THREAD_DEAD); /* See task_info() */ task_remove_thread(thread->task, thread); @@ -1981,15 +1972,29 @@ thread_destroy(struct thread *thread) static void thread_join_common(struct thread *thread) { - assert(thread != thread_self()); + struct thread_runq *runq; + unsigned long flags, state; + struct thread *self; - mutex_lock(&thread->join_lock); + self = thread_self(); + assert(thread != self); + + spinlock_lock(&thread->join_lock); - while (!thread->exited) { - condition_wait(&thread->join_cond, &thread->join_lock); + assert(!thread->join_waiter); + thread->join_waiter = self; + + while (!thread->exiting) { + thread_sleep(&thread->join_lock, thread, "exit"); } - mutex_unlock(&thread->join_lock); + spinlock_unlock(&thread->join_lock); + + do { + runq = thread_lock_runq(thread, &flags); + state = thread->state; + thread_unlock_runq(runq, flags); + } while (state != THREAD_DEAD); thread_unref(thread); } @@ -2386,13 +2391,17 @@ thread_exit(void) work_schedule(&zombie.work, 0); } - mutex_lock(&thread->join_lock); - thread->exited = 1; - condition_signal(&thread->join_cond); + /* + * Disable preemption before waking up since step 2 of the termination + * protocol involves actively polling the thread state. + */ + thread_preempt_disable(); - mutex_unlock(&thread->join_lock); + spinlock_lock(&thread->join_lock); + thread->exiting = true; + thread_wakeup(thread->join_waiter); + spinlock_unlock(&thread->join_lock); - thread_preempt_disable(); runq = thread_runq_local(); spinlock_lock_intr_save(&runq->lock, &flags); diff --git a/kern/thread_i.h b/kern/thread_i.h index 3f35088..7cbd3c7 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -169,10 +169,20 @@ struct thread { */ void *tsd[THREAD_KEYS_MAX]; - /* Members related to termination */ - struct mutex join_lock; - struct condition join_cond; /* (j) */ - int exited; /* (j) */ + /* + * Members related to termination. + * + * The termination protocol is made of two steps : + * 1/ The thread exits, thereby reporting that it is exiting while + * holding the join lock. This includes waking up any thread + * currently joining. + * 2/ The thread sets its state to dead and calls the scheduler. + * The join operation polls the state, and releases a reference + * when it sees the dead state. + */ + struct thread *join_waiter; /* (j) */ + struct spinlock join_lock; + bool exiting; /* (j) */ struct task *task; /* (T) */ struct list task_node; /* (T) */ -- cgit v1.2.3 From 1fcd8e7e40c3c10b4b694be3786b8a9cb7a38391 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 23 Aug 2017 21:57:22 +0200 Subject: kern/thread: really fix termination Change the semantics of thread_join so that it not only waits for thread_exit but also for the last reference to be dropped. --- kern/thread.c | 25 +++++++++++++------------ kern/thread.h | 2 +- kern/thread_i.h | 17 +++++++++-------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 78c75ba..17586ea 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1826,7 +1826,7 @@ thread_init(struct thread *thread, void *stack, memset(thread->tsd, 0, sizeof(thread->tsd)); thread->join_waiter = NULL; spinlock_init(&thread->join_lock); - thread->exiting = false; + thread->terminating = false; thread->task = task; thread->stack = stack; strlcpy(thread->name, attr->name, sizeof(thread->name)); @@ -1953,7 +1953,7 @@ thread_free_stack(void *stack) #endif /* X15_THREAD_STACK_GUARD */ -void +static void thread_destroy(struct thread *thread) { assert(thread != thread_self()); @@ -1984,7 +1984,7 @@ thread_join_common(struct thread *thread) assert(!thread->join_waiter); thread->join_waiter = self; - while (!thread->exiting) { + while (!thread->terminating) { thread_sleep(&thread->join_lock, thread, "exit"); } @@ -1996,7 +1996,15 @@ thread_join_common(struct thread *thread) thread_unlock_runq(runq, flags); } while (state != THREAD_DEAD); - thread_unref(thread); + thread_destroy(thread); +} + +void thread_terminate(struct thread *thread) +{ + spinlock_lock(&thread->join_lock); + thread->terminating = true; + thread_wakeup(thread->join_waiter); + spinlock_unlock(&thread->join_lock); } static void @@ -2391,16 +2399,9 @@ thread_exit(void) work_schedule(&zombie.work, 0); } - /* - * Disable preemption before waking up since step 2 of the termination - * protocol involves actively polling the thread state. - */ thread_preempt_disable(); - spinlock_lock(&thread->join_lock); - thread->exiting = true; - thread_wakeup(thread->join_waiter); - spinlock_unlock(&thread->join_lock); + thread_unref(thread); runq = thread_runq_local(); spinlock_lock_intr_save(&runq->lock, &flags); diff --git a/kern/thread.h b/kern/thread.h index 43a98e8..f565c15 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -291,7 +291,7 @@ thread_unref(struct thread *thread) assert(nr_refs != 0); if (nr_refs == 1) { - thread_destroy(thread); + thread_terminate(thread); } } diff --git a/kern/thread_i.h b/kern/thread_i.h index 7cbd3c7..cd34977 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -173,16 +173,17 @@ struct thread { * Members related to termination. * * The termination protocol is made of two steps : - * 1/ The thread exits, thereby reporting that it is exiting while - * holding the join lock. This includes waking up any thread - * currently joining. - * 2/ The thread sets its state to dead and calls the scheduler. - * The join operation polls the state, and releases a reference - * when it sees the dead state. + * 1/ The thread exits, thereby releasing its self reference, and + * sets its state to dead before calling the scheduler. + * 2/ Another thread must either already be joining, or join later. + * When the thread reference counter drops to zero, the terminating + * flag is set, and the joining thread is awaken, if any. After that, + * the join operation polls the state until it sees the target thread + * as dead, and then releases its resources. */ struct thread *join_waiter; /* (j) */ struct spinlock join_lock; - bool exiting; /* (j) */ + bool terminating; /* (j) */ struct task *task; /* (T) */ struct list task_node; /* (T) */ @@ -192,7 +193,7 @@ struct thread { #define THREAD_ATTR_DETACHED 0x1 -void thread_destroy(struct thread *thread); +void thread_terminate(struct thread *thread); /* * Flag access functions. -- cgit v1.2.3 From 8119ef95c94aafdec81e4646b9425922107e73b7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 25 Aug 2017 21:47:46 +0200 Subject: kern/thread: add a stack debugging assertion on scheduling --- kern/thread.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kern/thread.c b/kern/thread.c index 17586ea..fba95e0 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -609,6 +609,8 @@ thread_runq_schedule(struct thread_runq *runq) prev = thread_self(); + assert((__builtin_frame_address(0) >= prev->stack) + && (__builtin_frame_address(0) < (prev->stack + TCB_STACK_SIZE))); assert(prev->preempt == THREAD_SUSPEND_PREEMPT_LEVEL); assert(!cpu_intr_enabled()); spinlock_assert_locked(&runq->lock); -- cgit v1.2.3 From 094319b4a0a04ae11e24b44bb67aaf901536afb2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 25 Aug 2017 21:48:37 +0200 Subject: kern/thread: don't trigger priority propagation on return from interrupt --- kern/thread.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/kern/thread.h b/kern/thread.h index f565c15..1c05236 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -573,15 +573,23 @@ thread_preempt_enable_no_resched(void) assert(thread->preempt != 0); thread->preempt--; - if (thread_preempt_enabled() && thread_priority_propagation_needed()) { - thread_propagate_priority(); - } + /* + * Don't perform priority propagation here, because this function is + * called on return from interrupt, where the transient state may + * incorrectly trigger it. + */ } static inline void thread_preempt_enable(void) { thread_preempt_enable_no_resched(); + + if (thread_priority_propagation_needed() + && thread_preempt_enabled()) { + thread_propagate_priority(); + } + thread_schedule(); } -- cgit v1.2.3 From 2691b6088bddb12c4a17d08628e0af0a96b59b7f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:07:29 +0200 Subject: kern/clock: new module --- Makefrag.am | 3 ++ arch/x86/machine/lapic.c | 8 +-- kern/clock.c | 99 ++++++++++++++++++++++++++++++++++++++ kern/clock.h | 123 +++++++++++++++++++++++++++++++++++++++++++++++ kern/clock_i.h | 42 ++++++++++++++++ kern/thread.c | 16 ++---- kern/thread.h | 15 ++---- 7 files changed, 280 insertions(+), 26 deletions(-) create mode 100644 kern/clock.c create mode 100644 kern/clock.h create mode 100644 kern/clock_i.h diff --git a/Makefrag.am b/Makefrag.am index 1b39b72..336060d 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -20,6 +20,9 @@ x15_SOURCES += \ kern/bitmap_i.h \ kern/cbuf.c \ kern/cbuf.h \ + kern/clock.c \ + kern/clock.h \ + kern/clock_i.h \ kern/condition.c \ kern/condition.h \ kern/condition_types.h \ diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c index a0eee85..1a81a18 100644 --- a/arch/x86/machine/lapic.c +++ b/arch/x86/machine/lapic.c @@ -20,11 +20,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -211,7 +211,7 @@ lapic_compute_freq(void) lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY); log_info("lapic: bus frequency: %u.%02u MHz", lapic_bus_freq / 1000000, lapic_bus_freq % 1000000); - lapic_write(&lapic_map->timer_icr, lapic_bus_freq / THREAD_TICK_FREQ); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / CLOCK_FREQ); lapic_write(&lapic_map->svr, 0); } @@ -238,7 +238,7 @@ lapic_setup_registers(void) lapic_write(&lapic_map->lvt_lint1, LAPIC_LVT_MASK_INTR); lapic_write(&lapic_map->lvt_error, TRAP_LAPIC_ERROR); lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1); - lapic_write(&lapic_map->timer_icr, lapic_bus_freq / THREAD_TICK_FREQ); + lapic_write(&lapic_map->timer_icr, lapic_bus_freq / CLOCK_FREQ); } void __init @@ -333,7 +333,7 @@ lapic_timer_intr(struct trap_frame *frame) (void)frame; lapic_eoi(); - thread_tick_intr(); + clock_tick_intr(); } void diff --git a/kern/clock.c b/kern/clock.c new file mode 100644 index 0000000..d8af3f4 --- /dev/null +++ b/kern/clock.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 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 3 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 +#include + +struct clock_cpu_data { + struct syscnt sc_tick_intrs; +}; + +static struct clock_cpu_data clock_cpu_data __percpu; + +union clock_global_time clock_global_time; + +static inline void __init +clock_cpu_data_init(struct clock_cpu_data *cpu_data, unsigned int cpu) +{ + char name[SYSCNT_NAME_SIZE]; + + snprintf(name, sizeof(name), "clock_tick_intrs/%u", cpu); + syscnt_register(&cpu_data->sc_tick_intrs, name); +} + +static int __init +clock_setup(void) +{ + for (unsigned int cpu = 0; cpu < cpu_count(); cpu++) { + clock_cpu_data_init(percpu_ptr(clock_cpu_data, cpu), cpu); + } + + return 0; +} + +INIT_OP_DEFINE(clock_setup, + INIT_OP_DEP(boot_setup_intr, true), + INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(syscnt_setup, true)); + +void clock_tick_intr(void) +{ + struct clock_cpu_data *cpu_data; + + assert(!cpu_intr_enabled()); + assert(!thread_preempt_enabled()); + + if (cpu_id() == 0) { +#ifdef ATOMIC_HAVE_64B_OPS + + atomic_add(&clock_global_time.ticks, 1, ATOMIC_RELAXED); + +#else /* ATOMIC_HAVE_64B_OPS */ + + union clock_global_time t; + + t.ticks = clock_global_time.ticks; + t.ticks++; + + atomic_store(&clock_global_time.high2, t.high1, ATOMIC_RELAXED); + atomic_store_release(&clock_global_time.low, t.low); + atomic_store_release(&clock_global_time.high1, t.high1); + +#endif /* ATOMIC_HAVE_64B_OPS */ + } + + llsync_report_periodic_event(); + sref_report_periodic_event(); + work_report_periodic_event(); + thread_report_periodic_event(); + + cpu_data = cpu_local_ptr(clock_cpu_data); + syscnt_inc(&cpu_data->sc_tick_intrs); +} diff --git a/kern/clock.h b/kern/clock.h new file mode 100644 index 0000000..30db0a8 --- /dev/null +++ b/kern/clock.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Timekeeping module. + */ + +#ifndef _KERN_CLOCK_H +#define _KERN_CLOCK_H + +#include +#include + +#include +#include +#include +#include + +/* + * Clock frequency. + * + * TODO Clock frequency selection. + */ +#define CLOCK_FREQ 200 + +#if (1000 % CLOCK_FREQ) != 0 +#error "invalid clock frequency" +#endif /* (1000 % CLOCK_FREQ) != 0 */ + +/* + * Arbitrary value used to determine if a time is in the past or the future. + * + * Time is represented as 64-bits unsigned integers counting ticks. The + * global time currently starts from 0 but this isn't a strong assumption + * users should rely on. Instead, all time checks involve a time reference + * against which to compare. The result of that comparison, done by + * substraction, is either in the future, i.e. the difference is less + * than the expire threshold, or in the past, i.e. the difference is + * greater (keep in mind the result is unsigned). The threshold must be + * large enough to allow both a wide range of possible times in the future, + * but also enough time in the past for reliable timeout detection. Note + * that using signed integers would be equivalent to dividing the range + * in two (almost) equal past and future halves. + */ +#define CLOCK_EXPIRE_THRESHOLD (-(1ULL << 60)) + +static inline uint64_t +clock_get_time(void) +{ + extern union clock_global_time clock_global_time; + +#ifdef ATOMIC_HAVE_64B_OPS + + /* + * Don't enforce a stronger memory order, since : + * 1/ it's useless as long as the reader remains on the same processor + * 2/ thread migration enforces sequential consistency + */ + return atomic_load(&clock_global_time.ticks, ATOMIC_RELAXED); + +#else /* ATOMIC_HAVE_64B_OPS */ + + uint32_t high1, low, high2; + + /* + * For machines with no 64-bits atomic accessors, this implementation uses + * a variant of the two-digit monotonic-clock algorithm, described in the + * paper "Concurrent Reading and Writing of Clocks" by Leslie Lamport. + */ + + do { + high1 = atomic_load_acquire(&clock_global_time.high1); + low = atomic_load_acquire(&clock_global_time.low); + high2 = atomic_load(&clock_global_time.high2, ATOMIC_RELAXED); + } while (high1 != high2); + + return ((uint64_t)high2 << 32) | low; + +#endif /* ATOMIC_HAVE_64B_OPS */ +} + +static inline uint64_t +clock_ticks_to_ms(uint64_t ticks) +{ + return ticks * (1000 / CLOCK_FREQ); +} + +static inline uint64_t +clock_ticks_from_ms(uint64_t ms) +{ + return DIV_CEIL(ms, (1000 / CLOCK_FREQ)); +} + +static inline bool +clock_time_expired(uint64_t t, uint64_t ref) +{ + return (t - ref) > CLOCK_EXPIRE_THRESHOLD; +} + +static inline bool +clock_time_occurred(uint64_t t, uint64_t ref) +{ + return (t == ref) || clock_time_expired(t, ref); +} + +void clock_tick_intr(void); + +INIT_OP_DECLARE(clock_setup); + +#endif /* _KERN_CLOCK_H */ diff --git a/kern/clock_i.h b/kern/clock_i.h new file mode 100644 index 0000000..830f48c --- /dev/null +++ b/kern/clock_i.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_CLOCK_I_H +#define _KERN_CLOCK_I_H + +#include + +#include + +union clock_global_time { + alignas(CPU_L1_SIZE) uint64_t ticks; + +#ifndef ATOMIC_HAVE_64B_OPS + struct { +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint32_t high1; + uint32_t low; +#else /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ + uint32_t low; + uint32_t high1; +#endif /* __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ */ + uint32_t high2; + }; +#endif /* ATOMIC_HAVE_64B_OPS */ +}; + +#endif /* _KERN_CLOCK_I_H */ diff --git a/kern/thread.c b/kern/thread.c index fba95e0..2a1e124 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -91,6 +91,7 @@ #include #include +#include #include #include #include @@ -160,7 +161,7 @@ /* * Default time slice for real-time round-robin scheduling. */ -#define THREAD_DEFAULT_RR_TIME_SLICE (THREAD_TICK_FREQ / 10) +#define THREAD_DEFAULT_RR_TIME_SLICE (CLOCK_FREQ / 10) /* * Maximum number of threads which can be pulled from a remote run queue @@ -171,7 +172,7 @@ /* * Delay (in ticks) between two balance attempts when a run queue is idle. */ -#define THREAD_IDLE_BALANCE_TICKS (THREAD_TICK_FREQ / 2) +#define THREAD_IDLE_BALANCE_TICKS (CLOCK_FREQ / 2) /* * Run queue properties for real-time threads. @@ -191,7 +192,7 @@ struct thread_rt_runq { /* * Round slice base unit for fair-scheduling threads. */ -#define THREAD_FS_ROUND_SLICE_BASE (THREAD_TICK_FREQ / 10) +#define THREAD_FS_ROUND_SLICE_BASE (CLOCK_FREQ / 10) /* * Group of threads sharing the same weight. @@ -257,7 +258,6 @@ struct thread_runq { unsigned int idle_balance_ticks; struct syscnt sc_schedule_intrs; - struct syscnt sc_tick_intrs; struct syscnt sc_boosts; }; @@ -451,8 +451,6 @@ thread_runq_init(struct thread_runq *runq, unsigned int cpu, runq->idle_balance_ticks = (unsigned int)-1; snprintf(name, sizeof(name), "thread_schedule_intrs/%u", cpu); syscnt_register(&runq->sc_schedule_intrs, name); - snprintf(name, sizeof(name), "thread_tick_intrs/%u", cpu); - syscnt_register(&runq->sc_tick_intrs, name); snprintf(name, sizeof(name), "thread_boosts/%u", cpu); syscnt_register(&runq->sc_boosts, name); } @@ -2570,7 +2568,7 @@ thread_schedule_intr(void) } void -thread_tick_intr(void) +thread_report_periodic_event(void) { const struct thread_sched_ops *ops; struct thread_runq *runq; @@ -2579,10 +2577,6 @@ thread_tick_intr(void) thread_assert_interrupted(); runq = thread_runq_local(); - syscnt_inc(&runq->sc_tick_intrs); - llsync_report_periodic_event(); - sref_report_periodic_event(); - work_report_periodic_event(); thread = thread_self(); spinlock_lock(&runq->lock); diff --git a/kern/thread.h b/kern/thread.h index 1c05236..b3bf93f 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -47,14 +47,6 @@ #include #include -/* - * Scheduler tick frequency. - * - * The selected value of 200 translates to a period of 5ms, small enough to - * provide low latency, and is practical as both a dividend and divisor. - */ -#define THREAD_TICK_FREQ 200 - /* * Thread structure. */ @@ -253,10 +245,11 @@ void thread_yield(void); void thread_schedule_intr(void); /* - * Report a periodic timer interrupt on the thread currently running on - * the local processor. + * Report a periodic event on the current processor. + * + * Interrupts and preemption must be disabled when calling this function. */ -void thread_tick_intr(void); +void thread_report_periodic_event(void); /* * Set thread scheduling parameters. -- cgit v1.2.3 From 3824ad00551b43083c39638ce62ece92c34bae94 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:16:00 +0200 Subject: kern/timer: new module --- Makefrag.am | 3 + kern/clock.c | 2 + kern/timer.c | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kern/timer.h | 103 +++++++++++ kern/timer_i.h | 41 +++++ 5 files changed, 685 insertions(+) create mode 100644 kern/timer.c create mode 100644 kern/timer.h create mode 100644 kern/timer_i.h diff --git a/Makefrag.am b/Makefrag.am index 336060d..f29e592 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -111,6 +111,9 @@ x15_SOURCES += \ kern/thread.c \ kern/thread.h \ kern/thread_i.h \ + kern/timer.c \ + kern/timer.h \ + kern/timer_i.h \ kern/turnstile.c \ kern/turnstile.h \ kern/turnstile_types.h \ diff --git a/kern/clock.c b/kern/clock.c index d8af3f4..b4a6f04 100644 --- a/kern/clock.c +++ b/kern/clock.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,7 @@ void clock_tick_intr(void) #endif /* ATOMIC_HAVE_64B_OPS */ } + timer_report_periodic_event(); llsync_report_periodic_event(); sref_report_periodic_event(); work_report_periodic_event(); diff --git a/kern/timer.c b/kern/timer.c new file mode 100644 index 0000000..25f59f2 --- /dev/null +++ b/kern/timer.c @@ -0,0 +1,536 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * This implementation is based on "Hashed and Hierarchical Timing Wheels: + * Efficient Data Structures for Implementing a Timer Facility" by George + * Varghese and Tony Lauck. Specifically, it implements scheme 6.1.2. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Timer states. + */ +#define TIMER_TS_READY 1 +#define TIMER_TS_SCHEDULED 2 +#define TIMER_TS_RUNNING 3 +#define TIMER_TS_DONE 4 + +/* + * Timer flags. + */ +#define TIMER_TF_DETACHED 0x1 +#define TIMER_TF_INTR 0x2 +#define TIMER_TF_HIGH_PRIO 0x4 +#define TIMER_TF_CANCELED 0x8 + +#define TIMER_HTABLE_SIZE 2048 + +#if !ISP2(TIMER_HTABLE_SIZE) +#error "hash table size must be a power of two" +#endif /* !ISP2(TIMER_HTABLE_SIZE) */ + +#define TIMER_HTABLE_MASK (TIMER_HTABLE_SIZE - 1) + +struct timer_bucket { + struct hlist timers; +}; + +/* + * The hash table bucket matching the last time member has already been + * processed, and the next periodic event resumes from the next bucket. + * + * Locking order: interrupts -> timer_cpu_data. + */ +struct timer_cpu_data { + unsigned int cpu; + struct spinlock lock; + uint64_t last_time; + struct timer_bucket htable[TIMER_HTABLE_SIZE]; +}; + +static struct timer_cpu_data timer_cpu_data __percpu; + +static struct timer_cpu_data * +timer_lock_cpu_data(struct timer *timer, unsigned long *flagsp) +{ + struct timer_cpu_data *cpu_data; + + cpu_data = percpu_ptr(timer_cpu_data, timer->cpu); + spinlock_lock_intr_save(&cpu_data->lock, flagsp); + return cpu_data; +} + +static void +timer_unlock_cpu_data(struct timer_cpu_data *cpu_data, unsigned long flags) +{ + spinlock_unlock_intr_restore(&cpu_data->lock, flags); +} + +/* + * Timer state functions. + */ + +static bool +timer_ready(const struct timer *timer) +{ + return timer->state == TIMER_TS_READY; +} + +static void +timer_set_ready(struct timer *timer) +{ + timer->state = TIMER_TS_READY; +} + +static bool +timer_scheduled(const struct timer *timer) +{ + return timer->state == TIMER_TS_SCHEDULED; +} + +static void +timer_set_scheduled(struct timer *timer, unsigned int cpu) +{ + timer->cpu = cpu; + timer->state = TIMER_TS_SCHEDULED; +} + +static bool +timer_running(const struct timer *timer) +{ + return timer->state == TIMER_TS_RUNNING; +} + +static void +timer_set_running(struct timer *timer) +{ + timer->state = TIMER_TS_RUNNING; +} + +static bool +timer_done(const struct timer *timer) +{ + return timer->state == TIMER_TS_DONE; +} + +static void +timer_set_done(struct timer *timer) +{ + timer->state = TIMER_TS_DONE; +} + +/* + * Timer flags functions. + */ + +static bool +timer_detached(const struct timer *timer) +{ + return timer->flags & TIMER_TF_DETACHED; +} + +static void +timer_set_detached(struct timer *timer) +{ + timer->flags |= TIMER_TF_DETACHED; +} + +static bool +timer_is_intr(const struct timer *timer) +{ + return timer->flags & TIMER_TF_INTR; +} + +static void +timer_set_intr(struct timer *timer) +{ + timer->flags |= TIMER_TF_INTR; +} + +static bool +timer_is_high_prio(const struct timer *timer) +{ + return timer->flags & TIMER_TF_HIGH_PRIO; +} + +static void +timer_set_high_prio(struct timer *timer) +{ + timer->flags |= TIMER_TF_HIGH_PRIO; +} + +static bool +timer_canceled(const struct timer *timer) +{ + return timer->flags & TIMER_TF_CANCELED; +} + +static void +timer_set_canceled(struct timer *timer) +{ + timer->flags |= TIMER_TF_CANCELED; +} + +static void +timer_set_time(struct timer *timer, uint64_t ticks) +{ + timer->ticks = ticks; +} + +static bool +timer_occurred(const struct timer *timer, uint64_t ref) +{ + return clock_time_occurred(timer->ticks, ref); +} + +static uintptr_t +timer_hash(uint64_t ticks) +{ + return ticks; +} + +static void +timer_run(struct timer *timer) +{ + struct timer_cpu_data *cpu_data; + unsigned long cpu_flags; + + assert(timer_running(timer)); + + timer->fn(timer); + + if (timer_detached(timer)) { + return; + } + + cpu_data = timer_lock_cpu_data(timer, &cpu_flags); + + /* + * The timer handler may have : + * - rescheduled itself + * - been canceled + * - none of the above + * + * If the handler didn't call a timer function, or if the timer was + * canceled, set the state to done and wake up the joiner, if any. + * + * If the handler rescheduled the timer, nothing must be done. This + * is also true if the timer was canceled after being rescheduled by + * the handler (in this case, cancellation won't wait for a signal). + * These cases can be identified by checking if the timer state is + * different from running. + */ + + if (timer_running(timer)) { + timer_set_done(timer); + thread_wakeup(timer->joiner); + } + + timer_unlock_cpu_data(cpu_data, cpu_flags); +} + +static void +timer_run_work(struct work *work) +{ + struct timer *timer; + + timer = structof(work, struct timer, work); + timer_run(timer); +} + +static void +timer_process(struct timer *timer) +{ + int work_flags; + + if (timer_is_intr(timer)) { + timer_run(timer); + return; + } + + if (timer_is_high_prio(timer)) { + work_flags = TIMER_TF_HIGH_PRIO; + } else { + work_flags = 0; + } + + work_init(&timer->work, timer_run_work); + work_schedule(&timer->work, work_flags); +} + +static void +timer_bucket_init(struct timer_bucket *bucket) +{ + hlist_init(&bucket->timers); +} + +static void +timer_bucket_add(struct timer_bucket *bucket, struct timer *timer) +{ + hlist_insert_head(&bucket->timers, &timer->node); +} + +static void +timer_bucket_remove(struct timer_bucket *bucket, struct timer *timer) +{ + (void)bucket; + hlist_remove(&timer->node); +} + +static void +timer_cpu_data_init(struct timer_cpu_data *cpu_data, unsigned int cpu) +{ + cpu_data->cpu = cpu; + spinlock_init(&cpu_data->lock); + + /* See periodic event handling */ + cpu_data->last_time = clock_get_time() - 1; + + for (size_t i = 0; i < ARRAY_SIZE(cpu_data->htable); i++) { + timer_bucket_init(&cpu_data->htable[i]); + } +} + +static struct timer_cpu_data * +timer_cpu_data_acquire_local(unsigned long *flags) +{ + struct timer_cpu_data *cpu_data; + + thread_pin(); + cpu_data = cpu_local_ptr(timer_cpu_data); + spinlock_lock_intr_save(&cpu_data->lock, flags); + return cpu_data; +} + +static void +timer_cpu_data_release_local(struct timer_cpu_data *cpu_data, + unsigned long flags) +{ + spinlock_unlock_intr_restore(&cpu_data->lock, flags); + thread_unpin(); +} + +static struct timer_bucket * +timer_cpu_data_get_bucket(struct timer_cpu_data *cpu_data, uint64_t ticks) +{ + uintptr_t index; + + index = timer_hash(ticks) & TIMER_HTABLE_MASK; + assert(index < ARRAY_SIZE(cpu_data->htable)); + return &cpu_data->htable[index]; +} + +static void +timer_cpu_data_add(struct timer_cpu_data *cpu_data, struct timer *timer) +{ + struct timer_bucket *bucket; + + assert(timer_ready(timer)); + + bucket = timer_cpu_data_get_bucket(cpu_data, timer->ticks); + timer_bucket_add(bucket, timer); +} + +static void +timer_cpu_data_remove(struct timer_cpu_data *cpu_data, struct timer *timer) +{ + struct timer_bucket *bucket; + + assert(timer_scheduled(timer)); + + bucket = timer_cpu_data_get_bucket(cpu_data, timer->ticks); + timer_bucket_remove(bucket, timer); +} + +static void +timer_bucket_filter(struct timer_bucket *bucket, uint64_t now, + struct hlist *timers) +{ + struct timer *timer, *tmp; + + hlist_for_each_entry_safe(&bucket->timers, timer, tmp, node) { + assert(timer_scheduled(timer)); + + if (!timer_occurred(timer, now)) { + continue; + } + + hlist_remove(&timer->node); + timer_set_running(timer); + hlist_insert_head(timers, &timer->node); + } +} + +static int __init +timer_setup(void) +{ + for (unsigned int cpu = 0; cpu < cpu_count(); cpu++) { + timer_cpu_data_init(percpu_ptr(timer_cpu_data, cpu), cpu); + } + + return 0; +} + +INIT_OP_DEFINE(timer_setup, + INIT_OP_DEP(boot_setup_intr, true), + INIT_OP_DEP(cpu_mp_probe, true)); + +void timer_init(struct timer *timer, timer_fn_t fn, int flags) +{ + timer->fn = fn; + timer->state = TIMER_TS_READY; + timer->flags = 0; + timer->joiner = NULL; + + if (flags & TIMER_DETACHED) { + timer_set_detached(timer); + } + + if (flags & TIMER_INTR) { + timer_set_intr(timer); + } else if (flags & TIMER_HIGH_PRIO) { + timer_set_high_prio(timer); + } +} + +void +timer_schedule(struct timer *timer, uint64_t ticks) +{ + struct timer_cpu_data *cpu_data; + unsigned long cpu_flags; + + cpu_data = timer_cpu_data_acquire_local(&cpu_flags); + + if (timer_canceled(timer)) { + goto out; + } + + /* + * If called from the handler, the timer is running. If rescheduled + * after completion, it's done. + */ + if (timer_running(timer) || timer_done(timer)) { + timer_set_ready(timer); + } + + timer_set_time(timer, ticks); + + if (timer_occurred(timer, cpu_data->last_time)) { + ticks = cpu_data->last_time + 1; + } + + timer_cpu_data_add(cpu_data, timer); + timer_set_scheduled(timer, cpu_data->cpu); + +out: + timer_cpu_data_release_local(cpu_data, cpu_flags); +} + +void +timer_cancel(struct timer *timer) +{ + struct timer_cpu_data *cpu_data; + unsigned long cpu_flags; + + assert(!timer_detached(timer)); + + cpu_data = timer_lock_cpu_data(timer, &cpu_flags); + + assert(timer->joiner == NULL); + + timer_set_canceled(timer); + + if (timer_scheduled(timer)) { + timer_cpu_data_remove(cpu_data, timer); + } else { + timer->joiner = thread_self(); + + while (!timer_done(timer)) { + if (timer_is_intr(timer)) { + timer_unlock_cpu_data(cpu_data, cpu_flags); + cpu_pause(); + cpu_data = timer_lock_cpu_data(timer, &cpu_flags); + } else { + thread_sleep(&cpu_data->lock, timer, "tmr_cncl"); + } + } + + assert(timer_done(timer)); + + timer->joiner = NULL; + } + + timer_set_ready(timer); + + timer_unlock_cpu_data(cpu_data, cpu_flags); +} + +void +timer_report_periodic_event(void) +{ + struct timer_cpu_data *cpu_data; + struct timer_bucket *bucket; + struct timer *timer; + struct hlist timers; + uint64_t ticks, now; + + assert(!cpu_intr_enabled()); + assert(!thread_preempt_enabled()); + + now = clock_get_time(); + hlist_init(&timers); + cpu_data = cpu_local_ptr(timer_cpu_data); + + spinlock_lock(&cpu_data->lock); + + for (ticks = cpu_data->last_time + 1; + clock_time_occurred(ticks, now); + ticks++) { + bucket = timer_cpu_data_get_bucket(cpu_data, ticks); + timer_bucket_filter(bucket, now, &timers); + } + + cpu_data->last_time = now; + + spinlock_unlock(&cpu_data->lock); + + while (!hlist_empty(&timers)) { + timer = hlist_first_entry(&timers, struct timer, node); + hlist_remove(&timer->node); + timer_process(timer); + } +} diff --git a/kern/timer.h b/kern/timer.h new file mode 100644 index 0000000..4661668 --- /dev/null +++ b/kern/timer.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * Low resolution timer system. + */ + +#ifndef _KERN_TIMER_H +#define _KERN_TIMER_H + +#include + +#include + +/* + * Scheduling flags. + */ +#define TIMER_DETACHED 0x1 /* Timer completion isn't synchronized */ +#define TIMER_INTR 0x2 /* Handler is run from interrupt context */ +#define TIMER_HIGH_PRIO 0x4 /* Handler is run in high priority thread */ + +struct timer; + +/* + * Type for timer functions. + */ +typedef void (*timer_fn_t)(struct timer *); + +#include + +/* + * Return the absolute expiration time of the timer, in ticks. + */ +static inline uint64_t +timer_get_time(const struct timer *timer) +{ + return timer->ticks; /* TODO atomic */ +} + +/* + * Initialize a timer. + * + * Timers that are reponsible for releasing their own resources must + * be detached. + */ +void timer_init(struct timer *timer, timer_fn_t fn, int flags); + +/* + * Schedule a timer. + * + * The time of expiration is an absolute time in ticks. + * + * Timers may safely be rescheduled after completion. Periodic timers are + * implemented by rescheduling from the handler. + * + * If the timer has been canceled, this function does nothing. A + * canceled timer must be reinitialized before being scheduled again. + */ +void timer_schedule(struct timer *timer, uint64_t ticks); + +/* + * Cancel a timer. + * + * The given timer must not be detached. + * + * If the timer has already expired, this function waits until the timer + * function completes, or returns immediately if the function has already + * completed. + * + * This function may safely be called from the timer handler, but not on + * the current timer. Canceling a timer from the handler is achieved by + * simply not rescheduling it. + */ +void timer_cancel(struct timer *timer); + +/* + * Report a periodic event on the current processor. + * + * Interrupts and preemption must be disabled when calling this function. + */ +void timer_report_periodic_event(void); + +/* + * This init operation provides : + * - timer initialization and scheduling + * - module fully initialized + */ +INIT_OP_DECLARE(timer_setup); + +#endif /* _KERN_TIMER_H */ diff --git a/kern/timer_i.h b/kern/timer_i.h new file mode 100644 index 0000000..4ed01f2 --- /dev/null +++ b/kern/timer_i.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017 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 3 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 _KERN_TIMER_I_H +#define _KERN_TIMER_I_H + +#include +#include + +#include +#include + +struct timer { + union { + struct hlist_node node; + struct work work; + }; + + uint64_t ticks; + timer_fn_t fn; + unsigned int cpu; + unsigned short state; + unsigned short flags; + struct thread *joiner; +}; + +#endif /* _KERN_TIMER_I_H */ -- cgit v1.2.3 From 000c3defddf008c495f089cb8194b99a10e599cd Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:35:44 +0200 Subject: kern/thread: implement timed sleeps --- kern/thread.c | 132 +++++++++++++++++++++++++++++++++++++++----------------- kern/thread.h | 13 ++++-- kern/thread_i.h | 1 + 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index 2a1e124..73e1cfb 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -109,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -2419,49 +2420,14 @@ thread_join(struct thread *thread) thread_join_common(thread); } -void -thread_sleep(struct spinlock *interlock, const void *wchan_addr, - const char *wchan_desc) -{ - struct thread_runq *runq; - struct thread *thread; - unsigned long flags; - - thread = thread_self(); - assert(thread->preempt == 1); - - runq = thread_runq_local(); - spinlock_lock_intr_save(&runq->lock, &flags); - - if (interlock != NULL) { - thread_preempt_disable(); - spinlock_unlock(interlock); - } - - thread_set_wchan(thread, wchan_addr, wchan_desc); - thread->state = THREAD_SLEEPING; - - runq = thread_runq_schedule(runq); - assert(thread->state == THREAD_RUNNING); - - spinlock_unlock_intr_restore(&runq->lock, flags); - - if (interlock != NULL) { - spinlock_lock(interlock); - thread_preempt_enable_no_resched(); - } - - assert(thread->preempt == 1); -} - -void -thread_wakeup(struct thread *thread) +static int +thread_wakeup_common(struct thread *thread, int error) { struct thread_runq *runq; unsigned long flags; if ((thread == NULL) || (thread == thread_self())) { - return; + return ERROR_INVAL; } /* @@ -2477,7 +2443,7 @@ thread_wakeup(struct thread *thread) if (thread->state == THREAD_RUNNING) { thread_unlock_runq(runq, flags); - return; + return ERROR_INVAL; } thread_clear_wchan(thread); @@ -2495,10 +2461,98 @@ thread_wakeup(struct thread *thread) spinlock_lock(&runq->lock); } + thread->wakeup_error = error; thread_runq_wakeup(runq, thread); spinlock_unlock(&runq->lock); cpu_intr_restore(flags); thread_preempt_enable(); + + return 0; +} + +int +thread_wakeup(struct thread *thread) +{ + return thread_wakeup_common(thread, 0); +} + +struct thread_timeout_waiter { + struct thread *thread; + struct timer timer; +}; + +static void +thread_timeout(struct timer *timer) +{ + struct thread_timeout_waiter *waiter; + + waiter = structof(timer, struct thread_timeout_waiter, timer); + thread_wakeup_common(waiter->thread, ERROR_TIMEDOUT); +} + +static int +thread_sleep_common(struct spinlock *interlock, const void *wchan_addr, + const char *wchan_desc, bool timed, uint64_t ticks) +{ + struct thread_timeout_waiter waiter; + struct thread_runq *runq; + struct thread *thread; + unsigned long flags; + + thread = thread_self(); + assert(thread->preempt == 1); + + if (timed) { + waiter.thread = thread; + timer_init(&waiter.timer, thread_timeout, TIMER_INTR); + timer_schedule(&waiter.timer, ticks); + } + + runq = thread_runq_local(); + spinlock_lock_intr_save(&runq->lock, &flags); + + if (interlock != NULL) { + thread_preempt_disable(); + spinlock_unlock(interlock); + } + + thread_set_wchan(thread, wchan_addr, wchan_desc); + thread->state = THREAD_SLEEPING; + + runq = thread_runq_schedule(runq); + assert(thread->state == THREAD_RUNNING); + + spinlock_unlock_intr_restore(&runq->lock, flags); + + if (timed) { + timer_cancel(&waiter.timer); + } + + if (interlock != NULL) { + spinlock_lock(interlock); + thread_preempt_enable_no_resched(); + } + + assert(thread->preempt == 1); + + return thread->wakeup_error; +} + +void +thread_sleep(struct spinlock *interlock, const void *wchan_addr, + const char *wchan_desc) +{ + int error; + + error = thread_sleep_common(interlock, wchan_addr, wchan_desc, false, 0); + assert(!error); +} + +int +thread_timedsleep(struct spinlock *interlock, const void *wchan_addr, + const char *wchan_desc, uint64_t ticks) +{ + return thread_sleep_common(interlock, wchan_addr, wchan_desc, true, ticks); } void __init diff --git a/kern/thread.h b/kern/thread.h index b3bf93f..04eea2b 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -210,18 +211,24 @@ void thread_join(struct thread *thread); * address should refer to a relevant synchronization object, normally * containing the interlock, but not necessarily. * + * When bounding the duration of the sleep, the caller must pass an absolute + * time in ticks, and ERROR_TIMEDOUT is returned if that time is reached + * before the thread is awaken. + * * Implies a memory barrier. */ void thread_sleep(struct spinlock *interlock, const void *wchan_addr, const char *wchan_desc); +int thread_timedsleep(struct spinlock *interlock, const void *wchan_addr, + const char *wchan_desc, uint64_t ticks); /* * Schedule a thread for execution on a processor. * - * No action is performed if the target thread is NULL, the calling thread, - * or already in the running state. + * If the target thread is NULL, the calling thread, or already in the + * running state, no action is performed and ERROR_INVAL is returned. */ -void thread_wakeup(struct thread *thread); +int thread_wakeup(struct thread *thread); /* * Start running threads on the local processor. diff --git a/kern/thread_i.h b/kern/thread_i.h index cd34977..9ff4b76 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -107,6 +107,7 @@ struct thread { bool in_runq; /* (r) */ const void *wchan_addr; /* (r) */ const char *wchan_desc; /* (r) */ + int wakeup_error; /* (r) */ unsigned short state; /* (r) */ /* Sleep queue available for lending */ -- cgit v1.2.3 From d3d0b5245942055aa7478d2adb20f1359ef772f7 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:40:12 +0200 Subject: kern/sleepq: implement timed waits This change introduces the sleepq_broadcast and sleepq_wakeup functions because it's now impossible for a condition variable implementation to implement broadcasting along with timed waits on top of the existing interface. --- kern/sleepq.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- kern/sleepq.h | 31 ++++++++++--- 2 files changed, 160 insertions(+), 18 deletions(-) diff --git a/kern/sleepq.c b/kern/sleepq.c index 99282d9..170b1a9 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -38,19 +38,28 @@ struct sleepq_bucket { struct list list; }; +struct sleepq_waiter { + struct list node; + struct thread *thread; + bool pending_wakeup; +}; + +/* + * Waiters are queued in FIFO order and inserted at the head of the + * list of waiters. The pointer to the "oldest" waiter is used as + * a marker between threads waiting for a signal/broadcast (from the + * beginning up to and including the oldest waiter) and threads pending + * for wake-up (all the following threads up to the end of the list). + */ struct sleepq { - struct sleepq_bucket *bucket; + alignas(CPU_L1_SIZE) struct sleepq_bucket *bucket; struct list node; const void *sync_obj; struct list waiters; + struct sleepq_waiter *oldest_waiter; struct sleepq *next_free; }; -struct sleepq_waiter { - struct list node; - struct thread *thread; -}; - #define SLEEPQ_HTABLE_SIZE 128 #define SLEEPQ_COND_HTABLE_SIZE 64 @@ -76,11 +85,28 @@ static void sleepq_waiter_init(struct sleepq_waiter *waiter, struct thread *thread) { waiter->thread = thread; + waiter->pending_wakeup = false; +} + +static bool +sleepq_waiter_pending_wakeup(const struct sleepq_waiter *waiter) +{ + return waiter->pending_wakeup; +} + +static void +sleepq_waiter_set_pending_wakeup(struct sleepq_waiter *waiter) +{ + waiter->pending_wakeup = true; } static void sleepq_waiter_wakeup(struct sleepq_waiter *waiter) { + if (!sleepq_waiter_pending_wakeup(waiter)) { + return; + } + thread_wakeup(waiter->thread); } @@ -90,6 +116,7 @@ sleepq_assert_init_state(const struct sleepq *sleepq) assert(sleepq->bucket == NULL); assert(sleepq->sync_obj == NULL); assert(list_empty(&sleepq->waiters)); + assert(sleepq->oldest_waiter == NULL); assert(sleepq->next_free == NULL); } @@ -190,6 +217,7 @@ sleepq_ctor(void *ptr) sleepq->bucket = NULL; sleepq->sync_obj = NULL; list_init(&sleepq->waiters); + sleepq->oldest_waiter = NULL; sleepq->next_free = NULL; } @@ -366,16 +394,39 @@ sleepq_return(struct sleepq *sleepq, unsigned long flags) thread_sleepq_return(free_sleepq); } +static void +sleepq_shift_oldest_waiter(struct sleepq *sleepq) +{ + struct list *node; + + assert(sleepq->oldest_waiter != NULL); + + node = list_prev(&sleepq->oldest_waiter->node); + + if (list_end(&sleepq->waiters, node)) { + sleepq->oldest_waiter = NULL; + } else { + sleepq->oldest_waiter = list_entry(node, struct sleepq_waiter, node); + } +} + static void sleepq_add_waiter(struct sleepq *sleepq, struct sleepq_waiter *waiter) { list_insert_head(&sleepq->waiters, &waiter->node); + + if (sleepq->oldest_waiter == NULL) { + sleepq->oldest_waiter = waiter; + } } static void sleepq_remove_waiter(struct sleepq *sleepq, struct sleepq_waiter *waiter) { - (void)sleepq; + if (sleepq->oldest_waiter == waiter) { + sleepq_shift_oldest_waiter(sleepq); + } + list_remove(&waiter->node); } @@ -385,19 +436,48 @@ sleepq_empty(const struct sleepq *sleepq) return list_empty(&sleepq->waiters); } -void -sleepq_wait(struct sleepq *sleepq, const char *wchan) +static int +sleepq_wait_common(struct sleepq *sleepq, const char *wchan, + bool timed, uint64_t ticks) { struct sleepq_waiter waiter; struct thread *thread; + int error; thread = thread_self(); sleepq_waiter_init(&waiter, thread); sleepq_add_waiter(sleepq, &waiter); - thread_sleep(&sleepq->bucket->lock, sleepq->sync_obj, wchan); + if (!timed) { + thread_sleep(&sleepq->bucket->lock, sleepq->sync_obj, wchan); + error = 0; + } else { + error = thread_timedsleep(&sleepq->bucket->lock, sleepq->sync_obj, + wchan, ticks); + + if (error && sleepq_waiter_pending_wakeup(&waiter)) { + error = 0; + } + } sleepq_remove_waiter(sleepq, &waiter); + + return error; +} + +void +sleepq_wait(struct sleepq *sleepq, const char *wchan) +{ + int error; + + error = sleepq_wait_common(sleepq, wchan, false, 0); + assert(!error); +} + +int +sleepq_timedwait(struct sleepq *sleepq, const char *wchan, uint64_t ticks) +{ + return sleepq_wait_common(sleepq, wchan, true, ticks); } void @@ -405,10 +485,55 @@ sleepq_signal(struct sleepq *sleepq) { struct sleepq_waiter *waiter; - if (sleepq_empty(sleepq)) { + if (list_empty(&sleepq->waiters)) { return; } + waiter = list_last_entry(&sleepq->waiters, struct sleepq_waiter, node); + sleepq_waiter_set_pending_wakeup(waiter); + sleepq_waiter_wakeup(waiter); +} + +static void +sleepq_wakeup_common(struct sleepq *sleepq) +{ + struct sleepq_waiter *waiter; + + assert(!list_empty(&sleepq->waiters)); + waiter = list_last_entry(&sleepq->waiters, struct sleepq_waiter, node); sleepq_waiter_wakeup(waiter); } + +void +sleepq_broadcast(struct sleepq *sleepq) +{ + struct sleepq_waiter *waiter; + + if (sleepq->oldest_waiter == NULL) { + goto out; + } + + list_for_each_entry(&sleepq->waiters, waiter, node) { + sleepq_waiter_set_pending_wakeup(waiter); + + if (waiter == sleepq->oldest_waiter) { + break; + } + } + + sleepq->oldest_waiter = NULL; + +out: + sleepq_wakeup_common(sleepq); +} + +void +sleepq_wakeup(struct sleepq *sleepq) +{ + if (list_empty(&sleepq->waiters)) { + return; + } + + sleepq_wakeup_common(sleepq); +} diff --git a/kern/sleepq.h b/kern/sleepq.h index 3de765a..827d043 100644 --- a/kern/sleepq.h +++ b/kern/sleepq.h @@ -26,17 +26,13 @@ * the associated mutex, at which point two sleep queues are locked. * Handling condition variable sleep queues slightly differently * allows preventing deadlocks while keeping overall complexity low. - * - * In addition, despite being used to implement condition variables, - * this implementation doesn't provide a broadcast call. The rationale - * is to force users to implement "chained waking" in order to avoid - * the thundering herd effect. */ #ifndef _KERN_SLEEPQ_H #define _KERN_SLEEPQ_H #include +#include #include @@ -98,7 +94,7 @@ void sleepq_return(struct sleepq *sleepq, unsigned long flags); bool sleepq_empty(const struct sleepq *sleepq); /* - * Wait for a wake up on the given sleep queue. + * Wait for a wake-up on the given sleep queue. * * The sleep queue must be lent when calling this function. It is * released and later reacquired before returning from this function. @@ -110,8 +106,13 @@ bool sleepq_empty(const struct sleepq *sleepq); * the queue, the queue is not immediately considered empty. * * Threads are queued in FIFO order. + * + * When bounding the duration of the wait, the caller must pass an absolute + * time in ticks, and ERROR_TIMEDOUT is returned if that time is reached + * before the sleep queue is signalled. */ void sleepq_wait(struct sleepq *sleepq, const char *wchan); +int sleepq_timedwait(struct sleepq *sleepq, const char *wchan, uint64_t ticks); /* * Wake up a thread waiting on the given sleep queue, if any. @@ -125,10 +126,26 @@ void sleepq_wait(struct sleepq *sleepq, const char *wchan); * * Threads are queued in FIFO order, which means signalling a sleep * queue multiple times always awakens the same thread, regardless - * of new waiters, as long as that first thread didn't reacquire the + * of new waiters, as long as that thread didn't reacquire the * sleep queue. + * + * A broadcast differs only by also making all currently waiting threads + * pending for wake-up. As with sleepq_signal, a single thread may be + * awaken. The rationale is to force users to implement "chained waking" + * in order to avoid the thundering herd effect. */ void sleepq_signal(struct sleepq *sleepq); +void sleepq_broadcast(struct sleepq *sleepq); + +/* + * Wake up a pending thread. + * + * This function may only wake up a thread pending for wake-up after a + * broadcast. It is used to chain wake-ups to avoid the thundering herd + * effect. If there are no threads pending for wake-up, this function + * does nothing. + */ +void sleepq_wakeup(struct sleepq *sleepq); /* * This init operation provides : -- cgit v1.2.3 From 791a6563cd955f59e04084f1fc20aadbbc6ae25f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:45:37 +0200 Subject: kern/condition: implement timed waits --- kern/condition.c | 110 +++++++++++++++---------------------------------- kern/condition.h | 17 ++++---- kern/condition_types.h | 3 +- 3 files changed, 44 insertions(+), 86 deletions(-) diff --git a/kern/condition.c b/kern/condition.c index c8ea5f3..e6d6595 100644 --- a/kern/condition.c +++ b/kern/condition.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,52 +29,14 @@ #include #include -static void -condition_inc_nr_sleeping_waiters(struct condition *condition) -{ - condition->nr_sleeping_waiters++; - assert(condition->nr_sleeping_waiters != 0); -} - -static void -condition_dec_nr_sleeping_waiters(struct condition *condition) -{ - assert(condition->nr_sleeping_waiters != 0); - condition->nr_sleeping_waiters--; -} - -static void -condition_inc_nr_pending_waiters(struct condition *condition) -{ - condition->nr_pending_waiters++; - assert(condition->nr_pending_waiters != 0); -} - -static void -condition_dec_nr_pending_waiters(struct condition *condition) -{ - assert(condition->nr_pending_waiters != 0); - condition->nr_pending_waiters--; -} - -static void -condition_move_waiters(struct condition *condition) -{ - unsigned short old; - - assert(condition->nr_sleeping_waiters != 0); - old = condition->nr_pending_waiters; - condition->nr_pending_waiters += condition->nr_sleeping_waiters; - assert(old < condition->nr_pending_waiters); - condition->nr_sleeping_waiters = 0; -} - -void -condition_wait(struct condition *condition, struct mutex *mutex) +static int +condition_wait_common(struct condition *condition, struct mutex *mutex, + bool timed, uint64_t ticks) { struct condition *last_cond; struct sleepq *sleepq; unsigned long flags; + int error; mutex_assert_locked(mutex); @@ -101,23 +64,41 @@ condition_wait(struct condition *condition, struct mutex *mutex) if (last_cond != NULL) { assert(last_cond == condition); - - if (condition->nr_pending_waiters != 0) { - sleepq_signal(sleepq); - } + sleepq_wakeup(sleepq); } - condition_inc_nr_sleeping_waiters(condition); - sleepq_wait(sleepq, "cond"); - condition_dec_nr_pending_waiters(condition); + if (timed) { + error = sleepq_timedwait(sleepq, "cond", ticks); + } else { + sleepq_wait(sleepq, "cond"); + error = 0; + } - if (condition->nr_pending_waiters != 0) { + if (!error) { thread_set_last_cond(condition); } sleepq_return(sleepq, flags); mutex_lock(mutex); + + return error; +} + +void +condition_wait(struct condition *condition, struct mutex *mutex) +{ + int error; + + error = condition_wait_common(condition, mutex, false, 0); + assert(!error); +} + +int +condition_timedwait(struct condition *condition, + struct mutex *mutex, uint64_t ticks) +{ + return condition_wait_common(condition, mutex, true, ticks); } void @@ -132,16 +113,8 @@ condition_signal(struct condition *condition) return; } - if (condition->nr_sleeping_waiters == 0) { - goto out; - } - sleepq_signal(sleepq); - condition_dec_nr_sleeping_waiters(condition); - condition_inc_nr_pending_waiters(condition); - -out: sleepq_release(sleepq, flags); } @@ -157,15 +130,8 @@ condition_broadcast(struct condition *condition) return; } - if (condition->nr_sleeping_waiters == 0) { - goto out; - } - - sleepq_signal(sleepq); - - condition_move_waiters(condition); + sleepq_broadcast(sleepq); -out: sleepq_release(sleepq, flags); } @@ -181,17 +147,7 @@ condition_wakeup(struct condition *condition) return; } - if (condition->nr_pending_waiters == 0) { - goto out; - } - - /* - * Rely on the FIFO ordering of sleep queues so that signalling multiple - * times always wakes up the same thread, as long as that thread didn't - * reacquire the sleep queue. - */ - sleepq_signal(sleepq); + sleepq_wakeup(sleepq); -out: sleepq_release(sleepq, flags); } diff --git a/kern/condition.h b/kern/condition.h index 0ce8d94..90a59f0 100644 --- a/kern/condition.h +++ b/kern/condition.h @@ -27,6 +27,8 @@ #ifndef _KERN_CONDITION_H #define _KERN_CONDITION_H +#include + #include #include @@ -35,20 +37,21 @@ struct condition; /* * Initialize a condition variable. */ -static inline void -condition_init(struct condition *condition) -{ - condition->nr_sleeping_waiters = 0; - condition->nr_pending_waiters = 0; -} +#define condition_init(c) ((void)(c)) /* - * Wait for a wake-up on the given condition variable. + * Wait for a signal on the given condition variable. * * The associated mutex must be locked when calling this function. * It is unlocked before waiting and relocked before returning. + * + * When bounding the duration of the wait, the caller must pass an absolute + * time in ticks, and ERROR_TIMEDOUT is returned if that time is reached + * before the sleep queue is signalled. */ void condition_wait(struct condition *condition, struct mutex *mutex); +int condition_timedwait(struct condition *condition, + struct mutex *mutex, uint64_t ticks); /* * Wake up one (signal) or all (broadcast) threads waiting on a diff --git a/kern/condition_types.h b/kern/condition_types.h index 13a2920..abd42f2 100644 --- a/kern/condition_types.h +++ b/kern/condition_types.h @@ -22,8 +22,7 @@ #define _KERN_CONDITION_TYPES_H struct condition { - unsigned short nr_sleeping_waiters; - unsigned short nr_pending_waiters; + unsigned int _unused; }; #endif /* _KERN_CONDITION_TYPES_H */ -- cgit v1.2.3 From c32a06eae07bd0bc0b4017ea5286b91d6518ec7f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:48:54 +0200 Subject: kern/mutex/mutex_plain: implement timed waits --- kern/mutex/mutex_plain.c | 55 +++++++++++++++++++++++++++++++++++++++++----- kern/mutex/mutex_plain_i.h | 16 ++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c index 5e4ba53..58fc487 100644 --- a/kern/mutex/mutex_plain.c +++ b/kern/mutex/mutex_plain.c @@ -15,20 +15,25 @@ * along with this program. If not, see . */ +#include #include #include +#include #include #include #include #include -void -mutex_plain_lock_slow(struct mutex *mutex) +static int +mutex_plain_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) { unsigned int state; struct sleepq *sleepq; unsigned long flags; + int error; + + error = 0; sleepq = sleepq_lend(mutex, false, &flags); @@ -39,14 +44,49 @@ mutex_plain_lock_slow(struct mutex *mutex) break; } - sleepq_wait(sleepq, "mutex"); + if (!timed) { + sleepq_wait(sleepq, "mutex"); + } else { + error = sleepq_timedwait(sleepq, "mutex", ticks); + + if (error) { + break; + } + } + } + + if (error) { + if (sleepq_empty(sleepq)) { + atomic_cas(&mutex->state, MUTEX_CONTENDED, + MUTEX_LOCKED, ATOMIC_RELAXED); + } + + goto out; } if (sleepq_empty(sleepq)) { atomic_store(&mutex->state, MUTEX_LOCKED, ATOMIC_RELAXED); } +out: sleepq_return(sleepq, flags); + + return error; +} + +void +mutex_plain_lock_slow(struct mutex *mutex) +{ + int error; + + error = mutex_plain_lock_slow_common(mutex, false, 0); + assert(!error); +} + +int +mutex_plain_timedlock_slow(struct mutex *mutex, uint64_t ticks) +{ + return mutex_plain_lock_slow_common(mutex, true, ticks); } void @@ -57,8 +97,11 @@ mutex_plain_unlock_slow(struct mutex *mutex) sleepq = sleepq_acquire(mutex, false, &flags); - if (sleepq != NULL) { - sleepq_signal(sleepq); - sleepq_release(sleepq, flags); + if (sleepq == NULL) { + return; } + + sleepq_signal(sleepq); + + sleepq_release(sleepq, flags); } diff --git a/kern/mutex/mutex_plain_i.h b/kern/mutex/mutex_plain_i.h index 4f112b8..58e565e 100644 --- a/kern/mutex/mutex_plain_i.h +++ b/kern/mutex/mutex_plain_i.h @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -71,6 +72,7 @@ mutex_plain_unlock_fast(struct mutex *mutex) } void mutex_plain_lock_slow(struct mutex *mutex); +int mutex_plain_timedlock_slow(struct mutex *mutex, uint64_t ticks); void mutex_plain_unlock_slow(struct mutex *mutex); /* @@ -98,6 +100,20 @@ mutex_impl_lock(struct mutex *mutex) } } +static inline int +mutex_impl_timedlock(struct mutex *mutex, uint64_t ticks) +{ + int error; + + error = mutex_plain_lock_fast(mutex); + + if (unlikely(error)) { + error = mutex_plain_timedlock_slow(mutex, ticks); + } + + return error; +} + static inline void mutex_impl_unlock(struct mutex *mutex) { -- cgit v1.2.3 From a509d86280d14b435640c446d81d85e53fd866e9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:52:41 +0200 Subject: kern/mutex/mutex_adaptive: implement timed waits --- kern/mutex/mutex_adaptive.c | 121 +++++++++++++++++++++++++++++++++++++----- kern/mutex/mutex_adaptive_i.h | 15 ++++++ 2 files changed, 124 insertions(+), 12 deletions(-) diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c index ffc4716..34fdd22 100644 --- a/kern/mutex/mutex_adaptive.c +++ b/kern/mutex/mutex_adaptive.c @@ -15,11 +15,14 @@ * along with this program. If not, see . */ +#include #include #include #include #include +#include +#include #include #include #include @@ -47,20 +50,23 @@ mutex_adaptive_is_owner(struct mutex *mutex, uintptr_t owner) return mutex_adaptive_get_thread(prev) == mutex_adaptive_get_thread(owner); } -void -mutex_adaptive_lock_slow(struct mutex *mutex) +static int +mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) { uintptr_t self, owner; struct sleepq *sleepq; + struct thread *thread; unsigned long flags; + int error; + error = 0; self = (uintptr_t)thread_self(); sleepq = sleepq_lend(mutex, false, &flags); mutex_adaptive_set_contended(mutex); - for (;;) { + do { owner = atomic_cas_acquire(&mutex->owner, MUTEX_ADAPTIVE_CONTENDED, self | MUTEX_ADAPTIVE_CONTENDED); assert(owner & MUTEX_ADAPTIVE_CONTENDED); @@ -75,40 +81,131 @@ mutex_adaptive_lock_slow(struct mutex *mutex) */ while (mutex_adaptive_is_owner(mutex, owner)) { if (thread_is_running(mutex_adaptive_get_thread(owner))) { + if (timed && clock_time_occurred(ticks, clock_get_time())) { + error = ERROR_TIMEDOUT; + break; + } + cpu_pause(); } else { - sleepq_wait(sleepq, "mutex"); + if (!timed) { + sleepq_wait(sleepq, "mutex"); + } else { + error = sleepq_timedwait(sleepq, "mutex", ticks); + + if (error) { + break; + } + } } } - } + } while (!error); /* - * A potentially spinning thread wouldn't be accounted in the sleep queue, - * but the only potentially spinning thread is the new owner. + * Attempt to clear the contended bit. + * + * In case of success, the current thread becomes the new owner, and + * simply checking if the sleep queue is empty is enough. + * + * Keep in mind accesses to the mutex word aren't synchronized by + * the sleep queue, i.e. an unlock may occur completely concurrently + * while attempting to clear the contended bit . */ + + if (error) { + if (sleepq_empty(sleepq)) { + owner = atomic_load(&mutex->owner, ATOMIC_RELAXED); + assert(owner & MUTEX_ADAPTIVE_CONTENDED); + thread = mutex_adaptive_get_thread(owner); + + /* If there is an owner, try to clear the contended bit */ + if (thread != NULL) { + owner = atomic_cas(&mutex->owner, owner, + (uintptr_t)thread, ATOMIC_RELAXED); + assert(owner & MUTEX_ADAPTIVE_CONTENDED); + thread = mutex_adaptive_get_thread(owner); + } + + /* + * If there is no owner, the previous owner is currently unlocking + * the mutex, waiting for either a successful signal, or the + * value of the mutex to become different from the contended bit. + */ + if (thread == NULL) { + owner = atomic_cas(&mutex->owner, owner, 0, ATOMIC_RELAXED); + assert(owner == MUTEX_ADAPTIVE_CONTENDED); + } + } + + goto out; + } + if (sleepq_empty(sleepq)) { atomic_store(&mutex->owner, self, ATOMIC_RELAXED); } +out: sleepq_return(sleepq, flags); + + return error; +} + +void +mutex_adaptive_lock_slow(struct mutex *mutex) +{ + int error; + + error = mutex_adaptive_lock_slow_common(mutex, false, 0); + assert(!error); +} + +int +mutex_adaptive_timedlock_slow(struct mutex *mutex, uint64_t ticks) +{ + return mutex_adaptive_lock_slow_common(mutex, true, ticks); } void mutex_adaptive_unlock_slow(struct mutex *mutex) { - uintptr_t owner; + uintptr_t self, owner; struct sleepq *sleepq; unsigned long flags; + int error; - atomic_store(&mutex->owner, MUTEX_ADAPTIVE_CONTENDED, ATOMIC_RELEASE); + self = (uintptr_t)thread_self() | MUTEX_ADAPTIVE_CONTENDED; + + for (;;) { + owner = atomic_cas_release(&mutex->owner, self, + MUTEX_ADAPTIVE_CONTENDED); + + if (owner == self) { + break; + } else { + /* + * The contended bit was cleared after the fast path failed, + * but before the slow path (re)started. + */ + assert(owner == (uintptr_t)thread_self()); + error = mutex_adaptive_unlock_fast(mutex); + + if (error) { + continue; + } + + return; + } + } for (;;) { owner = atomic_load(&mutex->owner, ATOMIC_RELAXED); /* - * This only happens if another thread was able to become the new - * owner, in which case that thread isn't spinning on the current - * thread, i.e. there is no need for an additional reference. + * This only happens if : + * 1/ Another thread was able to become the new owner, in which + * case that thread isn't spinning on the current thread, i.e. + * there is no need for an additional reference. + * 2/ A timeout cleared the contended bit. */ if (owner != MUTEX_ADAPTIVE_CONTENDED) { break; diff --git a/kern/mutex/mutex_adaptive_i.h b/kern/mutex/mutex_adaptive_i.h index b9952ec..be822c2 100644 --- a/kern/mutex/mutex_adaptive_i.h +++ b/kern/mutex/mutex_adaptive_i.h @@ -78,6 +78,7 @@ mutex_adaptive_unlock_fast(struct mutex *mutex) } void mutex_adaptive_lock_slow(struct mutex *mutex); +int mutex_adaptive_timedlock_slow(struct mutex *mutex, uint64_t ticks); void mutex_adaptive_unlock_slow(struct mutex *mutex); /* @@ -105,6 +106,20 @@ mutex_impl_lock(struct mutex *mutex) } } +static inline int +mutex_impl_timedlock(struct mutex *mutex, uint64_t ticks) +{ + int error; + + error = mutex_adaptive_lock_fast(mutex); + + if (unlikely(error)) { + error = mutex_adaptive_timedlock_slow(mutex, ticks); + } + + return error; +} + static inline void mutex_impl_unlock(struct mutex *mutex) { -- cgit v1.2.3 From 50c583b698c4d1d13d1c0537c350691b18dd7033 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:55:36 +0200 Subject: kern/semaphore: implement timed waits --- kern/semaphore.c | 37 ++++++++++++++++++++++++++++++++++--- kern/semaphore.h | 15 +++++++++++++++ kern/semaphore_i.h | 3 +++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/kern/semaphore.c b/kern/semaphore.c index 7e94daf..72e843a 100644 --- a/kern/semaphore.c +++ b/kern/semaphore.c @@ -15,19 +15,25 @@ * along with this program. If not, see . */ +#include #include #include +#include #include #include #include -void -semaphore_wait_slow(struct semaphore *semaphore) +static int +semaphore_wait_slow_common(struct semaphore *semaphore, + bool timed, uint64_t ticks) { struct sleepq *sleepq; unsigned long flags; unsigned int prev; + int error; + + error = 0; sleepq = sleepq_lend(semaphore, false, &flags); @@ -38,10 +44,35 @@ semaphore_wait_slow(struct semaphore *semaphore) break; } - sleepq_wait(sleepq, "sem"); + if (!timed) { + sleepq_wait(sleepq, "sem"); + } else { + error = sleepq_timedwait(sleepq, "sem", ticks); + + if (error) { + break; + } + } } sleepq_return(sleepq, flags); + + return error; +} + +void +semaphore_wait_slow(struct semaphore *semaphore) +{ + int error; + + error = semaphore_wait_slow_common(semaphore, false, 0); + assert(!error); +} + +int +semaphore_timedwait_slow(struct semaphore *semaphore, uint64_t ticks) +{ + return semaphore_wait_slow_common(semaphore, true, ticks); } void diff --git a/kern/semaphore.h b/kern/semaphore.h index e08927e..e1acbf2 100644 --- a/kern/semaphore.h +++ b/kern/semaphore.h @@ -33,6 +33,7 @@ #define _KERN_SEMAPHORE_H #include +#include #include #include @@ -93,6 +94,20 @@ semaphore_wait(struct semaphore *semaphore) } } +static inline int +semaphore_timedwait(struct semaphore *semaphore, uint64_t ticks) +{ + unsigned int prev; + + prev = semaphore_dec(semaphore); + + if (unlikely(prev == 0)) { + return semaphore_timedwait_slow(semaphore, ticks); + } + + return 0; +} + /* * Unlock a semaphore. * diff --git a/kern/semaphore_i.h b/kern/semaphore_i.h index acd7cd4..6e79b13 100644 --- a/kern/semaphore_i.h +++ b/kern/semaphore_i.h @@ -19,6 +19,7 @@ #define _KERN_SEMAPHORE_I_H #include +#include #include @@ -56,6 +57,8 @@ semaphore_inc(struct semaphore *semaphore) void semaphore_wait_slow(struct semaphore *semaphore); +int semaphore_timedwait_slow(struct semaphore *semaphore, uint64_t ticks); + void semaphore_post_slow(struct semaphore *semaphore); #endif /* _KERN_SEMAPHORE_I_H */ -- cgit v1.2.3 From b155465ec7984d8a3f8c07a5f548e457f31b6af3 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 16:58:16 +0200 Subject: kern/turnstile: implement timed waits --- kern/turnstile.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++------ kern/turnstile.h | 12 +++++++++-- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/kern/turnstile.c b/kern/turnstile.c index 1e66777..e59a7f3 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -196,7 +196,6 @@ static void turnstile_remove_waiter(struct turnstile *turnstile, struct turnstile_waiter *waiter) { - assert(turnstile_waiter_awaken(waiter)); plist_remove(&turnstile->waiters, &waiter->node); turnstile_update_top_waiter(turnstile); } @@ -283,6 +282,9 @@ turnstile_td_disown(struct turnstile_td *td, struct turnstile *turnstile) turnstile->owner = NULL; } +/* + * A turnstile must be "reowned" whenever its top waiter has changed. + */ static void turnstile_td_reown(struct turnstile_td *td, struct turnstile *turnstile) { @@ -407,7 +409,6 @@ turnstile_assert_init_state(const struct turnstile *turnstile) assert(plist_empty(&turnstile->waiters)); assert(turnstile->next_free == NULL); assert(turnstile->top_waiter == NULL); - assert(turnstile->owner == NULL); } static void @@ -675,7 +676,11 @@ turnstile_update_owner(struct turnstile *turnstile, struct thread *owner) thread_ref(owner); spinlock_lock(&td->lock); - if (turnstile->owner == NULL) { + if (turnstile_empty(turnstile)) { + if (turnstile->owner != NULL) { + turnstile_td_disown(td, turnstile); + } + } else if (turnstile->owner == NULL) { turnstile_td_own(td, turnstile); } else { turnstile_td_reown(td, turnstile); @@ -688,14 +693,16 @@ turnstile_update_owner(struct turnstile *turnstile, struct thread *owner) spinlock_lock(&turnstile->bucket->lock); } -void -turnstile_wait(struct turnstile *turnstile, const char *wchan, - struct thread *owner) +static int +turnstile_wait_common(struct turnstile *turnstile, const char *wchan, + struct thread *owner, bool timed, uint64_t ticks) { struct turnstile_waiter waiter; struct turnstile_td *td; struct thread *thread; + int error; + error = 0; thread = thread_self(); assert(thread != owner); @@ -718,9 +725,25 @@ turnstile_wait(struct turnstile *turnstile, const char *wchan, for (;;) { if (!turnstile_waiter_awaken(&waiter)) { - thread_sleep(&turnstile->bucket->lock, turnstile->sync_obj, wchan); + if (!timed) { + thread_sleep(&turnstile->bucket->lock, + turnstile->sync_obj, wchan); + } else { + error = thread_timedsleep(&turnstile->bucket->lock, + turnstile->sync_obj, wchan, ticks); + + if (error) { + if (turnstile_waiter_awaken(&waiter)) { + error = 0; + } else { + break; + } + } + } } + assert(turnstile_waiter_awaken(&waiter)); + /* * The real priority of a thread may change between waking up * and reacquiring the turnstile. @@ -738,6 +761,30 @@ turnstile_wait(struct turnstile *turnstile, const char *wchan, turnstile_td_set_waiter(td, NULL); turnstile_remove_waiter(turnstile, &waiter); spinlock_unlock(&td->lock); + + if (error && (turnstile->owner != NULL)) { + /* This function temporarily unlocks the turnstile */ + turnstile_update_owner(turnstile, turnstile->owner); + } + + return error; +} + +void +turnstile_wait(struct turnstile *turnstile, const char *wchan, + struct thread *owner) +{ + int error; + + error = turnstile_wait_common(turnstile, wchan, owner, false, 0); + assert(!error); +} + +int +turnstile_timedwait(struct turnstile *turnstile, const char *wchan, + struct thread *owner, uint64_t ticks) +{ + return turnstile_wait_common(turnstile, wchan, owner, true, ticks); } void @@ -783,6 +830,10 @@ turnstile_disown(struct turnstile *turnstile) struct turnstile_td *td; struct thread *owner; + if (turnstile->owner == NULL) { + return; + } + owner = thread_self(); assert(turnstile->owner == owner); assert(!turnstile_empty(turnstile)); diff --git a/kern/turnstile.h b/kern/turnstile.h index 0473a4c..e7b4a5e 100644 --- a/kern/turnstile.h +++ b/kern/turnstile.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -157,9 +158,17 @@ bool turnstile_empty(const struct turnstile *turnstile); * the associated synchronization object. The priority of the caller * is propagated to the chain of turnstiles and owners as necessary * to prevent unbounded priority inversion. + * + * When bounding the duration of the wait, the caller must pass an absolute + * time in ticks, and ERROR_TIMEDOUT is returned if that time is reached + * before the turnstile is signalled. In addition, if a timeout occurs, + * the calling thread temporarily releases the turnstile before returning, + * causing other threads to consider the turnstile as empty. */ void turnstile_wait(struct turnstile *turnstile, const char *wchan, struct thread *owner); +int turnstile_timedwait(struct turnstile *turnstile, const char *wchan, + struct thread *owner, uint64_t ticks); /* * Wake up a thread waiting on the given turnstile, if any. @@ -175,8 +184,7 @@ void turnstile_signal(struct turnstile *turnstile); * Own/disown a turnstile. * * The turnstile must be lent when taking ownership, acquired when - * releasing it. Owning has no effect on empty turnstiles. - * Conversely, an empty turnstile cannot be disowned. + * releasing it. * * Ownership must be updated atomically with regard to the ownership * of the associated synchronization object. -- cgit v1.2.3 From e395627038e10c6e966a141743b95d7c286b25bd Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:00:40 +0200 Subject: kern/rtmutex: implement timed waits --- kern/mutex/mutex_pi_i.h | 8 +++++ kern/rtmutex.c | 84 +++++++++++++++++++++++++++++++++++++++++++------ kern/rtmutex.h | 14 +++++++++ kern/rtmutex_i.h | 2 ++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/kern/mutex/mutex_pi_i.h b/kern/mutex/mutex_pi_i.h index 6c39db7..616f09b 100644 --- a/kern/mutex/mutex_pi_i.h +++ b/kern/mutex/mutex_pi_i.h @@ -23,6 +23,8 @@ " use instead" #endif +#include + #include #include @@ -51,6 +53,12 @@ mutex_impl_lock(struct mutex *mutex) rtmutex_lock(&mutex->rtmutex); } +static inline int +mutex_impl_timedlock(struct mutex *mutex, uint64_t ticks) +{ + return rtmutex_timedlock(&mutex->rtmutex, ticks); +} + static inline void mutex_impl_unlock(struct mutex *mutex) { diff --git a/kern/rtmutex.c b/kern/rtmutex.c index db23920..0070b93 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -16,6 +16,7 @@ */ #include +#include #include #include @@ -26,20 +27,28 @@ #include #include +static struct thread * +rtmutex_get_thread(uintptr_t owner) +{ + return (struct thread *)(owner & RTMUTEX_OWNER_MASK); +} + static void rtmutex_set_contended(struct rtmutex *rtmutex) { atomic_or(&rtmutex->owner, RTMUTEX_CONTENDED, ATOMIC_RELEASE); } -void -rtmutex_lock_slow(struct rtmutex *rtmutex) +static int +rtmutex_lock_slow_common(struct rtmutex *rtmutex, bool timed, uint64_t ticks) { struct turnstile *turnstile; uintptr_t self, owner; struct thread *thread; uintptr_t bits; + int error; + error = 0; self = (uintptr_t)thread_self(); turnstile = turnstile_lend(rtmutex); @@ -56,11 +65,40 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) break; } - thread = (struct thread *)(owner & RTMUTEX_OWNER_MASK); - turnstile_wait(turnstile, "rtmutex", thread); + thread = rtmutex_get_thread(owner); + + if (!timed) { + turnstile_wait(turnstile, "rtmutex", thread); + } else { + error = turnstile_timedwait(turnstile, "rtmutex", thread, ticks); + + if (error) { + break; + } + } + bits |= RTMUTEX_FORCE_WAIT; } + if (error) { + /* + * Keep in mind more than one thread may have timed out on waiting. + * These threads aren't considered waiters, making the turnstile + * empty. The first to reacquire the turnstile clears the contention + * bits, allowing the owner to unlock through the fast path. + */ + if (turnstile_empty(turnstile)) { + owner = atomic_load(&rtmutex->owner, ATOMIC_RELAXED); + + if (owner & RTMUTEX_CONTENDED) { + owner &= RTMUTEX_OWNER_MASK; + atomic_store(&rtmutex->owner, owner, ATOMIC_RELAXED); + } + } + + goto out; + } + turnstile_own(turnstile); if (turnstile_empty(turnstile)) { @@ -68,6 +106,7 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) assert(owner == (self | bits)); } +out: turnstile_return(turnstile); /* @@ -76,27 +115,54 @@ rtmutex_lock_slow(struct rtmutex *rtmutex) * introducing unbounded priority inversion. * Instead, let new waiters do it, using their own priority. */ + + return error; +} + +void +rtmutex_lock_slow(struct rtmutex *rtmutex) +{ + int error; + + error = rtmutex_lock_slow_common(rtmutex, false, 0); + assert(!error); +} + +int +rtmutex_timedlock_slow(struct rtmutex *rtmutex, uint64_t ticks) +{ + return rtmutex_lock_slow_common(rtmutex, true, ticks); } void rtmutex_unlock_slow(struct rtmutex *rtmutex) { struct turnstile *turnstile; - uintptr_t self, owner; + uintptr_t owner; - self = (uintptr_t)thread_self(); + for (;;) { + turnstile = turnstile_acquire(rtmutex); + + if (turnstile != NULL) { + break; + } + + owner = rtmutex_unlock_fast(rtmutex); - turnstile = turnstile_acquire(rtmutex); - assert(turnstile != NULL); + if (!(owner & RTMUTEX_CONTENDED)) { + return; + } + } owner = atomic_swap_release(&rtmutex->owner, RTMUTEX_FORCE_WAIT | RTMUTEX_CONTENDED); - assert((owner & RTMUTEX_OWNER_MASK) == self); + assert(rtmutex_get_thread(owner) == thread_self()); turnstile_disown(turnstile); turnstile_signal(turnstile); turnstile_release(turnstile); + /* TODO Make private, use thread_set_priority_propagation_needed instead */ thread_propagate_priority(); } diff --git a/kern/rtmutex.h b/kern/rtmutex.h index ec79afa..87cd15a 100644 --- a/kern/rtmutex.h +++ b/kern/rtmutex.h @@ -87,6 +87,20 @@ rtmutex_lock(struct rtmutex *rtmutex) } } +static inline int +rtmutex_timedlock(struct rtmutex *rtmutex, uint64_t ticks) +{ + uintptr_t prev_owner; + + prev_owner = rtmutex_lock_fast(rtmutex); + + if (unlikely(prev_owner != 0)) { + return rtmutex_timedlock_slow(rtmutex, ticks); + } + + return 0; +} + /* * Unlock a real-time mutex. * diff --git a/kern/rtmutex_i.h b/kern/rtmutex_i.h index 984cfd1..75ac5e4 100644 --- a/kern/rtmutex_i.h +++ b/kern/rtmutex_i.h @@ -74,6 +74,8 @@ rtmutex_unlock_fast(struct rtmutex *rtmutex) void rtmutex_lock_slow(struct rtmutex *rtmutex); +int rtmutex_timedlock_slow(struct rtmutex *rtmutex, uint64_t ticks); + void rtmutex_unlock_slow(struct rtmutex *rtmutex); #endif /* _KERN_RTMUTEX_I_H */ -- cgit v1.2.3 From dd43dd17d7fd82af9001a28709320dac7348f223 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:08:10 +0200 Subject: kern/mutex: add timed waits to the mutex interface --- kern/mutex.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/kern/mutex.h b/kern/mutex.h index f192a70..8cb7aac 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -27,6 +27,8 @@ #error "only one of X15_MUTEX_PI and X15_MUTEX_ADAPTIVE may be defined" #endif +#include + #if defined(X15_MUTEX_PI) #include #elif defined(X15_MUTEX_ADAPTIVE) @@ -76,6 +78,22 @@ mutex_lock(struct mutex *mutex) mutex_impl_lock(mutex); } +/* + * Lock a mutex, with a time boundary. + * + * The time boundary is an absolute time in ticks. + * + * If successful, the mutex is locked, otherwise an error is returned. + * A mutex can only be locked once. + * + * This function may sleep. + */ +static inline int +mutex_timedlock(struct mutex *mutex, uint64_t ticks) +{ + return mutex_impl_timedlock(mutex, ticks); +} + /* * Unlock a mutex. * -- cgit v1.2.3 From 20aa997e8347d5b37e1b3e39f79ac0b2309b506e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:10:04 +0200 Subject: kern/thread: add the thread_delay function --- kern/thread.c | 15 +++++++++++++++ kern/thread.h | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/kern/thread.c b/kern/thread.c index 73e1cfb..043f14d 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2555,6 +2555,21 @@ thread_timedsleep(struct spinlock *interlock, const void *wchan_addr, return thread_sleep_common(interlock, wchan_addr, wchan_desc, true, ticks); } +void +thread_delay(uint64_t ticks, bool absolute) +{ + thread_preempt_disable(); + + if (!absolute) { + /* Add a tick to avoid quantization errors */ + ticks += clock_get_time() + 1; + } + + thread_timedsleep(NULL, thread_self(), "delay", ticks); + + thread_preempt_enable(); +} + void __init thread_run_scheduler(void) { diff --git a/kern/thread.h b/kern/thread.h index 04eea2b..e84300c 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -230,6 +230,11 @@ int thread_timedsleep(struct spinlock *interlock, const void *wchan_addr, */ int thread_wakeup(struct thread *thread); +/* + * Suspend execution of the calling thread. + */ +void thread_delay(uint64_t ticks, bool absolute); + /* * Start running threads on the local processor. * -- cgit v1.2.3 From 0917f35f0e22f28d9dd78ff340df9e114e2ba45d Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:11:02 +0200 Subject: x86/pit: make the PIT a potential clock source --- arch/x86/machine/acpi.c | 4 +++- arch/x86/machine/pit.c | 43 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 7720226..4ef52e3 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -693,9 +694,10 @@ error: * For the sake of simplicity, it has been decided to ignore legacy * specifications such as the multiprocessor specification, and use * ACPI only. If ACPI is unavailable, consider the APIC system to - * be missing and fall back to using the legacy XT-PIC. + * be missing and fall back to using the legacy XT-PIC and PIT. */ pic_setup(); + pit_setup(); return 0; } diff --git a/arch/x86/machine/pit.c b/arch/x86/machine/pit.c index c31c7e0..8651330 100644 --- a/arch/x86/machine/pit.c +++ b/arch/x86/machine/pit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012 Richard Braun. + * Copyright (c) 2011-2017 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 @@ -17,7 +17,11 @@ #include +#include #include +#include +#include +#include #include #include @@ -45,19 +49,46 @@ */ #define PIT_MAX_COUNT 0xffff -void __init -pit_setup_free_running(void) +/* + * Timer interrupt. + */ +#define PIT_INTR 0 + +static int +pit_intr(void *arg) +{ + (void)arg; + + clock_tick_intr(); + return 0; +} + +static void __init +pit_setup_common(uint16_t count) { io_write_byte(PIT_PORT_MODE, PIT_MODE_RATE_GEN | PIT_MODE_RW_LSB | PIT_MODE_RW_MSB); - io_write_byte(PIT_PORT_COUNTER0, PIT_MAX_COUNT & 0xff); - io_write_byte(PIT_PORT_COUNTER0, PIT_MAX_COUNT >> 8); + io_write_byte(PIT_PORT_COUNTER0, count & 0xff); + io_write_byte(PIT_PORT_COUNTER0, count >> 8); +} + +void __init +pit_setup_free_running(void) +{ + pit_setup_common(PIT_MAX_COUNT); } void __init pit_setup(void) { - /* TODO Implement */ + int error; + + pit_setup_common(DIV_CEIL(PIT_FREQ, CLOCK_FREQ)); + error = intr_register(PIT_INTR, pit_intr, NULL); + + if (error) { + log_err("pit: unable to register interrupt handler"); + } } static unsigned int -- cgit v1.2.3 From 93a8a5e35c4f5dc31316f034c83b34d25fa8bf08 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:15:21 +0200 Subject: doc/intro(9): quickly discuss time and timers --- doc/intro.9.txt | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 5af5a4e..7a486ed 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -102,6 +102,10 @@ module:kern/thread:: module:kern/work:: Work queue of deferred asynchronous lightweight jobs. +All wait functions on synchronization objects can be time-bounded. +This includes waiting for a mutex lock, a condition variable, or a +semaphore. + Mutex implementations ~~~~~~~~~~~~~~~~~~~~~ @@ -134,6 +138,8 @@ module:kern/bitmap:: Arbitrary-length bit array. module:kern/cbuf:: Circular character buffer. +module:kern/clock:: + Low resolution clock. module:kern/error:: Common errors and error handling functions. module:kern/hash:: @@ -158,6 +164,8 @@ module:kern/sprintf:: Formatted string functions. module:kern/syscnt:: Generic 64-bits counter. +module:kern/timer:: + Low resolution timer. X15 doesn't provide a generic queue interface, because the requirements often vary too much. Similarly, it doesn't provide a hash table interface. @@ -202,13 +210,13 @@ TODO Write when the virtual memory system is rewritten. REAL-TIME --------- -X15 complies with almost all the requirements of a true hard real-time -multiprocessor system. It is a fully preemptible kernel with short, -bounded preemption-based critical sections. It provides real-time -scheduling policies and a complete priority inheritance algorithm. -Preemption and interrupts are clearly decoupled so that interrupts -can remain enabled as much as possible. Multiprocessor synchronization -uses rigorously fair spin locks. The modules related to real-time are : +X15 complies with all the requirements of a real-time multiprocessor +system. It is a fully preemptible kernel with short, bounded +preemption-based critical sections. It provides real-time scheduling +policies and a complete priority inheritance algorithm. Preemption and +interrupts are clearly decoupled so that interrupts can remain enabled +as much as possible. Multiprocessor synchronization uses rigorously +fair spin locks. The modules related to real-time are : module:kern/rtmutex:: Mutual exclusion with priority inheritance. -- cgit v1.2.3 From 70f7512a01ba8f90aad6dbb4d285e279f0e17e64 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 27 Aug 2017 17:18:54 +0200 Subject: Add TODO entries --- Makefile.am | 1 + doc/intro.9.txt | 5 +++-- kern/rbtree.h | 2 ++ kern/thread.c | 2 +- kern/thread.h | 1 + kern/work.h | 2 ++ kern/xcall.c | 1 + 7 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 413ac6d..de3e574 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,7 @@ AM_CFLAGS += \ -fsigned-char \ -fno-common +# TODO Add option AM_CFLAGS += -fno-stack-protector AM_CFLAGS += -nostdlib diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 7a486ed..5b7ee2b 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -233,8 +233,7 @@ Priority inheritance can also be enabled for regular mutexes. Please read Victor Yodaiken's report {against-priority-inheritance} in order to fully understand the implications of relying on priority inheritance. -TODO X15 doesn't yet comply with all the requirements for hard real-time. -For that, it still needs a high resolution timer system. +TODO Separate man page with more description [[portability]] PORTABILITY @@ -267,6 +266,8 @@ the future. In addition, the machine-independent code assumes an almost completely relaxed memory model, but still expects no reordering between dependent loads. This model closely matches the ARM family of processors. +TODO Fix memory model description + [[posix_like_interface]] POSIX-LIKE INTERFACE -------------------- diff --git a/kern/rbtree.h b/kern/rbtree.h index 4ae8353..3de240b 100644 --- a/kern/rbtree.h +++ b/kern/rbtree.h @@ -265,6 +265,8 @@ rbtree_insert_slot(struct rbtree *tree, rbtree_slot_t slot, * Remove a node from a tree. * * After completion, the node is stale. + * + * TODO rbtree_replace. */ void rbtree_remove(struct rbtree *tree, struct rbtree_node *node); diff --git a/kern/thread.c b/kern/thread.c index 043f14d..e4d8f7c 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1861,7 +1861,7 @@ thread_lock_runq(struct thread *thread, unsigned long *flags) struct thread_runq *runq; for (;;) { - runq = thread->runq; + runq = thread->runq; /* TODO Atomic access */ spinlock_lock_intr_save(&runq->lock, flags); diff --git a/kern/thread.h b/kern/thread.h index e84300c..a3f2670 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -652,6 +652,7 @@ thread_intr_leave(void) } } +/* TODO Use in interrupt handlers instead of manual interrupt/preemption checks */ static inline void thread_assert_interrupted(void) { diff --git a/kern/work.h b/kern/work.h index a8df1f7..2d7bd62 100644 --- a/kern/work.h +++ b/kern/work.h @@ -46,6 +46,8 @@ typedef void (*work_fn_t)(struct work *); * This structure should be embedded in objects related to the work. It * stores the work function and is passed to it as its only parameter. * The function can then find the containing object with the structof macro. + * + * TODO Make private. */ struct work { struct work *next; diff --git a/kern/xcall.c b/kern/xcall.c index 44bd41f..b5ed4b2 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -133,6 +133,7 @@ xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu) thread_preempt_disable(); + /* TODO Fix to match interrupt context semantics */ if (cpu == cpu_id()) { unsigned long flags; -- cgit v1.2.3 From 08a8ebf87c58e7a247bae1ec1ece187103473f72 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 28 Aug 2017 20:20:53 +0200 Subject: doc/intro(9): add TODO entry --- doc/intro.9.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 5b7ee2b..e63ae9a 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -172,6 +172,9 @@ often vary too much. Similarly, it doesn't provide a hash table interface. Instead, users can easily build specialized queues, hash tables and ring buffers on top of the provided facilities. +TODO List supported standard C interfaces and remove the printf/sprintf +modules from the list above. + [[multiprocessor_support]] MULTIPROCESSOR SUPPORT ---------------------- -- cgit v1.2.3 From a9719450e83c2c64eecd097d82beb624948e9de9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 29 Aug 2017 00:48:11 +0200 Subject: test/test_mutex: new module --- Makefrag.am | 4 ++ configure.ac | 3 + test/test_mutex.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 test/test_mutex.c diff --git a/Makefrag.am b/Makefrag.am index f29e592..d799bb8 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -158,6 +158,10 @@ if TEST_LLSYNC_DEFER x15_SOURCES += test/test_llsync_defer.c endif TEST_LLSYNC_DEFER +if TEST_MUTEX +x15_SOURCES += test/test_mutex.c +endif TEST_MUTEX + if TEST_MUTEX_PI x15_SOURCES += test/test_mutex_pi.c endif TEST_MUTEX_PI diff --git a/configure.ac b/configure.ac index 2693959..947786c 100644 --- a/configure.ac +++ b/configure.ac @@ -77,6 +77,7 @@ AC_DEFINE_UNQUOTED([X15_ARCH], [$arch], [arch]) m4_define([ENABLE_TEST_MODULE], [AS_CASE(["$enable_test_module"], [llsync_defer], [test_llsync_defer=yes], + [mutex], [test_mutex=yes], [mutex_pi], [test_mutex_pi=yes], [pmap_update_mp], [test_pmap_update_mp=yes], [sref_dirty_zeroes], [test_sref_dirty_zeroes=yes], @@ -92,6 +93,8 @@ m4_define([ENABLE_TEST_MODULE], AS_IF([test x"$enable_test_module" != xno], [ENABLE_TEST_MODULE]) AM_CONDITIONAL([TEST_LLSYNC_DEFER], [test x"$test_llsync_defer" = xyes]) +AM_CONDITIONAL([TEST_MUTEX], + [test x"$test_mutex" = xyes]) AM_CONDITIONAL([TEST_MUTEX_PI], [test x"$test_mutex_pi" = xyes]) AM_CONDITIONAL([TEST_PMAP_UPDATE_MP], diff --git a/test/test_mutex.c b/test/test_mutex.c new file mode 100644 index 0000000..f24cc9e --- /dev/null +++ b/test/test_mutex.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017 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 3 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 . + * + * + * This test module is a stress test, expected to never terminate, of the + * timed lock functionality provided by the mutex implementations. The + * two conditions for success are : + * - no assertion triggered + * - all debugging system counters of the selected mutex implementation + * must be non-zero after some time. + * + * The system counters are meant to perform simple code coverage, asserting + * all the tricky code paths are taken at least once. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_REPORT_INTERVAL 10000 + +struct test { + struct mutex mutex; + unsigned int counter; +}; + +static struct timer test_timer; + +static void +test_run(void *arg) +{ + unsigned int prev, counter; + struct test *test; + int error; + + test = arg; + + for (counter = 1; /* no condition */; counter++) { + if ((counter % 1024) == 0) { + printf("%s ", thread_self()->name); + } + + error = mutex_timedlock(&test->mutex, clock_get_time() + 1); + + if (error) { + thread_delay(1, false); + continue; + } + + prev = atomic_fetch_add(&test->counter, 1, ATOMIC_SEQ_CST); + + if (prev != 0) { + break; + } + + if ((counter % 2) == 0) { + cpu_delay(clock_ticks_to_ms(1) * 1000); + } else { + thread_delay(1, false); + } + + prev = atomic_fetch_sub(&test->counter, 1, ATOMIC_SEQ_CST); + + if (prev != 1) { + break; + } + + mutex_unlock(&test->mutex); + + if ((counter % 2) == 0) { + thread_delay(1, false); + } + } + + panic("test: invalid counter value (%u)", test->counter); +} + +static struct test * +test_create(unsigned int nr_threads) +{ + char name[THREAD_NAME_SIZE]; + struct thread_attr attr; + struct thread *thread; + struct cpumap *cpumap; + struct test *test; + int error; + + assert(nr_threads); + + test = kmem_alloc(sizeof(*test)); + + if (!test) { + panic("test: unable to allocate memory"); + } + + mutex_init(&test->mutex); + test->counter = 0; + + error = cpumap_create(&cpumap); + error_check(error, "cpumap_create"); + + for (size_t i = 0; i < nr_threads; i++) { + cpumap_zero(cpumap); + cpumap_set(cpumap, i % 3); + snprintf(name, sizeof(name), THREAD_KERNEL_PREFIX "test_run:%u/%zu", + nr_threads, i); + thread_attr_init(&attr, name); + thread_attr_set_detached(&attr); + thread_attr_set_cpumap(&attr, cpumap); + + if (i < 2) { + thread_attr_set_policy(&attr, THREAD_SCHED_POLICY_RR); + thread_attr_set_priority(&attr, THREAD_SCHED_RT_PRIO_MIN + i); + } + + error = thread_create(&thread, &attr, test_run, test); + error_check(error, "thread_create"); + } + + return test; +} + +static void +test_report_syscnt(struct timer *timer) +{ + uint64_t time; + +#ifdef X15_MUTEX_PI + syscnt_info("rtmutex"); +#else /* X15_MUTEX_PI */ + syscnt_info("mutex"); +#endif /* X15_MUTEX_PI */ + + time = timer_get_time(timer) + clock_ticks_from_ms(TEST_REPORT_INTERVAL); + timer_schedule(timer, time); +} + +void +test_setup(void) +{ + uint64_t time; + + if (cpu_count() < 3) { + panic("test: at least 3 processors are required"); + } + + test_create(1); + test_create(2); + test_create(3); + test_create(10); + + timer_init(&test_timer, test_report_syscnt, TIMER_DETACHED); + time = clock_get_time() + clock_ticks_from_ms(TEST_REPORT_INTERVAL); + timer_schedule(&test_timer, time); + + log_info("test: enable mutex debugging for the selected implementation"); + log_info("test: and check the relevant system counters"); +} -- cgit v1.2.3 From 5daf8db5209b0849a7bf43d9d823c67be9a52bcf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 30 Aug 2017 23:27:31 +0200 Subject: Add debugging code to the mutex modules --- kern/mutex.c | 2 + kern/mutex/mutex_adaptive.c | 99 +++++++++++++++++++++++++++++++++++++++++++ kern/mutex/mutex_adaptive_i.h | 5 +++ kern/mutex/mutex_pi_i.h | 2 + kern/mutex/mutex_plain.c | 68 +++++++++++++++++++++++++++++ kern/mutex/mutex_plain_i.h | 5 +++ kern/rtmutex.c | 73 +++++++++++++++++++++++++++++++ kern/rtmutex.h | 3 ++ kern/syscnt.c | 7 ++- 9 files changed, 263 insertions(+), 1 deletion(-) diff --git a/kern/mutex.c b/kern/mutex.c index 6260976..bd548b8 100644 --- a/kern/mutex.c +++ b/kern/mutex.c @@ -16,6 +16,7 @@ */ #include +#include #include static int __init @@ -25,4 +26,5 @@ mutex_setup(void) } INIT_OP_DEFINE(mutex_setup, + INIT_OP_DEP(mutex_impl_setup, true), INIT_OP_DEP(thread_setup_booter, true)); diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c index 34fdd22..ceec8f1 100644 --- a/kern/mutex/mutex_adaptive.c +++ b/kern/mutex/mutex_adaptive.c @@ -23,12 +23,83 @@ #include #include #include +#include #include #include #include +#include #include #include +/* Set to 1 to enable debugging */ +#define MUTEX_ADAPTIVE_DEBUG 0 + +#if MUTEX_ADAPTIVE_DEBUG + +enum { + MUTEX_ADAPTIVE_SC_SPINS, + MUTEX_ADAPTIVE_SC_WAIT_SUCCESSES, + MUTEX_ADAPTIVE_SC_WAIT_ERRORS, + MUTEX_ADAPTIVE_SC_DOWNGRADES, + MUTEX_ADAPTIVE_SC_ERROR_DOWNGRADES, + MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS, + MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT, + MUTEX_ADAPTIVE_SC_FAST_UNLOCKS, + MUTEX_ADAPTIVE_SC_SLOW_UNLOCKS, + MUTEX_ADAPTIVE_SC_EXTERNAL_UNLOCKS, + MUTEX_ADAPTIVE_SC_SIGNALS, + MUTEX_ADAPTIVE_NR_SCS +}; + +static struct syscnt mutex_adaptive_sc_array[MUTEX_ADAPTIVE_NR_SCS]; + +static void +mutex_adaptive_register_sc(unsigned int index, const char *name) +{ + assert(index < ARRAY_SIZE(mutex_adaptive_sc_array)); + syscnt_register(&mutex_adaptive_sc_array[index], name); +} + +static void +mutex_adaptive_setup_debug(void) +{ + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_SPINS, + "mutex_adaptive_spins"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_WAIT_SUCCESSES, + "mutex_adaptive_wait_successes"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_WAIT_ERRORS, + "mutex_adaptive_wait_errors"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_DOWNGRADES, + "mutex_adaptive_downgrades"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_DOWNGRADES, + "mutex_adaptive_error_downgrades"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS, + "mutex_adaptive_error_clearbits"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT, + "mutex_adaptive_error_clearcont"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_FAST_UNLOCKS, + "mutex_adaptive_fast_unlocks"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_SLOW_UNLOCKS, + "mutex_adaptive_slow_unlocks"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_EXTERNAL_UNLOCKS, + "mutex_adaptive_external_unlocks"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_SIGNALS, + "mutex_adaptive_signals"); +} + +static void +mutex_adaptive_inc_sc(unsigned int index) +{ + assert(index < ARRAY_SIZE(mutex_adaptive_sc_array)); + syscnt_inc(&mutex_adaptive_sc_array[index]); +} + +#else /* MUTEX_ADAPTIVE_DEBUG */ +#define mutex_adaptive_setup_debug() +#define mutex_adaptive_inc_sc(x) +#endif /* MUTEX_ADAPTIVE_DEBUG */ + + static struct thread * mutex_adaptive_get_thread(uintptr_t owner) { @@ -81,6 +152,8 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) */ while (mutex_adaptive_is_owner(mutex, owner)) { if (thread_is_running(mutex_adaptive_get_thread(owner))) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_SPINS); + if (timed && clock_time_occurred(ticks, clock_get_time())) { error = ERROR_TIMEDOUT; break; @@ -113,13 +186,17 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) */ if (error) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_WAIT_ERRORS); + if (sleepq_empty(sleepq)) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_DOWNGRADES); owner = atomic_load(&mutex->owner, ATOMIC_RELAXED); assert(owner & MUTEX_ADAPTIVE_CONTENDED); thread = mutex_adaptive_get_thread(owner); /* If there is an owner, try to clear the contended bit */ if (thread != NULL) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS); owner = atomic_cas(&mutex->owner, owner, (uintptr_t)thread, ATOMIC_RELAXED); assert(owner & MUTEX_ADAPTIVE_CONTENDED); @@ -132,6 +209,7 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) * value of the mutex to become different from the contended bit. */ if (thread == NULL) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT); owner = atomic_cas(&mutex->owner, owner, 0, ATOMIC_RELAXED); assert(owner == MUTEX_ADAPTIVE_CONTENDED); } @@ -140,7 +218,10 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) goto out; } + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_WAIT_SUCCESSES); + if (sleepq_empty(sleepq)) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_DOWNGRADES); atomic_store(&mutex->owner, self, ATOMIC_RELAXED); } @@ -193,10 +274,13 @@ mutex_adaptive_unlock_slow(struct mutex *mutex) continue; } + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_FAST_UNLOCKS); return; } } + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_SLOW_UNLOCKS); + for (;;) { owner = atomic_load(&mutex->owner, ATOMIC_RELAXED); @@ -208,6 +292,7 @@ mutex_adaptive_unlock_slow(struct mutex *mutex) * 2/ A timeout cleared the contended bit. */ if (owner != MUTEX_ADAPTIVE_CONTENDED) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_EXTERNAL_UNLOCKS); break; } @@ -222,6 +307,7 @@ mutex_adaptive_unlock_slow(struct mutex *mutex) sleepq = sleepq_tryacquire(mutex, false, &flags); if (sleepq != NULL) { + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_SIGNALS); sleepq_signal(sleepq); sleepq_release(sleepq, flags); break; @@ -233,3 +319,16 @@ mutex_adaptive_unlock_slow(struct mutex *mutex) */ } } + +static int +mutex_adaptive_setup(void) +{ + mutex_adaptive_setup_debug(); + return 0; +} + +INIT_OP_DEFINE(mutex_adaptive_setup, +#if MUTEX_ADAPTIVE_DEBUG + INIT_OP_DEP(syscnt_setup, true), +#endif /* MUTEX_ADAPTIVE_DEBUG */ +); diff --git a/kern/mutex/mutex_adaptive_i.h b/kern/mutex/mutex_adaptive_i.h index be822c2..a8598e6 100644 --- a/kern/mutex/mutex_adaptive_i.h +++ b/kern/mutex/mutex_adaptive_i.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -132,4 +133,8 @@ mutex_impl_unlock(struct mutex *mutex) } } +#define mutex_impl_setup mutex_adaptive_setup + +INIT_OP_DECLARE(mutex_adaptive_setup); + #endif /* _KERN_MUTEX_ADAPTIVE_I_H */ diff --git a/kern/mutex/mutex_pi_i.h b/kern/mutex/mutex_pi_i.h index 616f09b..2d5a2b6 100644 --- a/kern/mutex/mutex_pi_i.h +++ b/kern/mutex/mutex_pi_i.h @@ -65,4 +65,6 @@ mutex_impl_unlock(struct mutex *mutex) rtmutex_unlock(&mutex->rtmutex); } +#define mutex_impl_setup rtmutex_setup + #endif /* _KERN_MUTEX_PI_I_H */ diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c index 58fc487..6a7cbdc 100644 --- a/kern/mutex/mutex_plain.c +++ b/kern/mutex/mutex_plain.c @@ -21,9 +21,58 @@ #include #include +#include #include #include #include +#include + +/* Set to 1 to enable debugging */ +#define MUTEX_PLAIN_DEBUG 0 + +#if MUTEX_PLAIN_DEBUG + +enum { + MUTEX_PLAIN_SC_WAIT_SUCCESSES, + MUTEX_PLAIN_SC_WAIT_ERRORS, + MUTEX_PLAIN_SC_DOWNGRADES, + MUTEX_PLAIN_SC_ERROR_DOWNGRADES, + MUTEX_PLAIN_NR_SCS +}; + +static struct syscnt mutex_plain_sc_array[MUTEX_PLAIN_NR_SCS]; + +static void +mutex_plain_register_sc(unsigned int index, const char *name) +{ + assert(index < ARRAY_SIZE(mutex_plain_sc_array)); + syscnt_register(&mutex_plain_sc_array[index], name); +} + +static void +mutex_plain_setup_debug(void) +{ + mutex_plain_register_sc(MUTEX_PLAIN_SC_WAIT_SUCCESSES, + "mutex_plain_wait_successes"); + mutex_plain_register_sc(MUTEX_PLAIN_SC_WAIT_ERRORS, + "mutex_plain_wait_errors"); + mutex_plain_register_sc(MUTEX_PLAIN_SC_DOWNGRADES, + "mutex_plain_downgrades"); + mutex_plain_register_sc(MUTEX_PLAIN_SC_ERROR_DOWNGRADES, + "mutex_plain_error_downgrades"); +} + +static void +mutex_plain_inc_sc(unsigned int index) +{ + assert(index < ARRAY_SIZE(mutex_plain_sc_array)); + syscnt_inc(&mutex_plain_sc_array[index]); +} + +#else /* MUTEX_PLAIN_DEBUG */ +#define mutex_plain_setup_debug() +#define mutex_plain_inc_sc(x) +#endif /* MUTEX_PLAIN_DEBUG */ static int mutex_plain_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) @@ -56,7 +105,10 @@ mutex_plain_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) } if (error) { + mutex_plain_inc_sc(MUTEX_PLAIN_SC_WAIT_ERRORS); + if (sleepq_empty(sleepq)) { + mutex_plain_inc_sc(MUTEX_PLAIN_SC_ERROR_DOWNGRADES); atomic_cas(&mutex->state, MUTEX_CONTENDED, MUTEX_LOCKED, ATOMIC_RELAXED); } @@ -64,7 +116,10 @@ mutex_plain_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) goto out; } + mutex_plain_inc_sc(MUTEX_PLAIN_SC_WAIT_SUCCESSES); + if (sleepq_empty(sleepq)) { + mutex_plain_inc_sc(MUTEX_PLAIN_SC_DOWNGRADES); atomic_store(&mutex->state, MUTEX_LOCKED, ATOMIC_RELAXED); } @@ -105,3 +160,16 @@ mutex_plain_unlock_slow(struct mutex *mutex) sleepq_release(sleepq, flags); } + +static int +mutex_plain_setup(void) +{ + mutex_plain_setup_debug(); + return 0; +} + +INIT_OP_DEFINE(mutex_plain_setup, +#if MUTEX_PLAIN_DEBUG + INIT_OP_DEP(syscnt_setup, true), +#endif /* MUTEX_PLAIN_DEBUG */ +); diff --git a/kern/mutex/mutex_plain_i.h b/kern/mutex/mutex_plain_i.h index 58e565e..fe97308 100644 --- a/kern/mutex/mutex_plain_i.h +++ b/kern/mutex/mutex_plain_i.h @@ -28,6 +28,7 @@ #include #include +#include #include #define MUTEX_UNLOCKED 0 @@ -126,4 +127,8 @@ mutex_impl_unlock(struct mutex *mutex) } } +#define mutex_impl_setup mutex_plain_setup + +INIT_OP_DECLARE(mutex_plain_setup); + #endif /* _KERN_MUTEX_PLAIN_I_H */ diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 0070b93..9fcc4e0 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -21,12 +21,64 @@ #include #include +#include +#include #include #include #include #include #include +/* Set to 1 to enable debugging */ +#define RTMUTEX_DEBUG 0 + +#if RTMUTEX_DEBUG + +enum { + RTMUTEX_SC_WAIT_SUCCESSES, + RTMUTEX_SC_WAIT_ERRORS, + RTMUTEX_SC_DOWNGRADES, + RTMUTEX_SC_ERROR_DOWNGRADES, + RTMUTEX_SC_CANCELED_DOWNGRADES, + RTMUTEX_NR_SCS +}; + +static struct syscnt rtmutex_sc_array[RTMUTEX_NR_SCS]; + +static void +rtmutex_register_sc(unsigned int index, const char *name) +{ + assert(index < ARRAY_SIZE(rtmutex_sc_array)); + syscnt_register(&rtmutex_sc_array[index], name); +} + +static void +rtmutex_setup_debug(void) +{ + rtmutex_register_sc(RTMUTEX_SC_WAIT_SUCCESSES, + "rtmutex_wait_successes"); + rtmutex_register_sc(RTMUTEX_SC_WAIT_ERRORS, + "rtmutex_wait_errors"); + rtmutex_register_sc(RTMUTEX_SC_DOWNGRADES, + "rtmutex_downgrades"); + rtmutex_register_sc(RTMUTEX_SC_ERROR_DOWNGRADES, + "rtmutex_error_downgrades"); + rtmutex_register_sc(RTMUTEX_SC_CANCELED_DOWNGRADES, + "rtmutex_canceled_downgrades"); +} + +static void +rtmutex_inc_sc(unsigned int index) +{ + assert(index < ARRAY_SIZE(rtmutex_sc_array)); + syscnt_inc(&rtmutex_sc_array[index]); +} + +#else /* RTMUTEX_DEBUG */ +#define rtmutex_setup_debug() +#define rtmutex_inc_sc(x) +#endif /* RTMUTEX_DEBUG */ + static struct thread * rtmutex_get_thread(uintptr_t owner) { @@ -81,6 +133,8 @@ rtmutex_lock_slow_common(struct rtmutex *rtmutex, bool timed, uint64_t ticks) } if (error) { + rtmutex_inc_sc(RTMUTEX_SC_WAIT_ERRORS); + /* * Keep in mind more than one thread may have timed out on waiting. * These threads aren't considered waiters, making the turnstile @@ -91,17 +145,23 @@ rtmutex_lock_slow_common(struct rtmutex *rtmutex, bool timed, uint64_t ticks) owner = atomic_load(&rtmutex->owner, ATOMIC_RELAXED); if (owner & RTMUTEX_CONTENDED) { + rtmutex_inc_sc(RTMUTEX_SC_ERROR_DOWNGRADES); owner &= RTMUTEX_OWNER_MASK; atomic_store(&rtmutex->owner, owner, ATOMIC_RELAXED); + } else { + rtmutex_inc_sc(RTMUTEX_SC_CANCELED_DOWNGRADES); } } goto out; } + rtmutex_inc_sc(RTMUTEX_SC_WAIT_SUCCESSES); + turnstile_own(turnstile); if (turnstile_empty(turnstile)) { + rtmutex_inc_sc(RTMUTEX_SC_DOWNGRADES); owner = atomic_swap(&rtmutex->owner, self, ATOMIC_RELAXED); assert(owner == (self | bits)); } @@ -166,3 +226,16 @@ rtmutex_unlock_slow(struct rtmutex *rtmutex) /* TODO Make private, use thread_set_priority_propagation_needed instead */ thread_propagate_priority(); } + +static int +rtmutex_setup(void) +{ + rtmutex_setup_debug(); + return 0; +} + +INIT_OP_DEFINE(rtmutex_setup, +#if RTMUTEX_DEBUG + INIT_OP_DEP(syscnt_setup, true), +#endif /* RTMUTEX_DEBUG */ + ); diff --git a/kern/rtmutex.h b/kern/rtmutex.h index 87cd15a..bc76b77 100644 --- a/kern/rtmutex.h +++ b/kern/rtmutex.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -119,4 +120,6 @@ rtmutex_unlock(struct rtmutex *rtmutex) } } +INIT_OP_DECLARE(rtmutex_setup); + #endif /* _KERN_RTMUTEX_H */ diff --git a/kern/syscnt.c b/kern/syscnt.c index 7cceaba..0e3aff7 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -25,6 +25,7 @@ #include #include #include +#include /* * Global list of all registered counters. @@ -70,8 +71,12 @@ syscnt_setup(void) return 0; } +/* + * Do not make initialization depend on mutex_setup, since mutex + * modules may use system counters for debugging. + */ INIT_OP_DEFINE(syscnt_setup, - INIT_OP_DEP(mutex_setup, true)); + INIT_OP_DEP(thread_setup_booter, true)); void __init syscnt_register(struct syscnt *syscnt, const char *name) -- cgit v1.2.3 From ec30a349de3645aaabb59517b15f542c2b7b9acd Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 00:36:50 +0200 Subject: kern/timer: fix locking --- kern/timer.c | 88 +++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/kern/timer.c b/kern/timer.c index 25f59f2..365b2fa 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,8 @@ #define TIMER_TF_HIGH_PRIO 0x4 #define TIMER_TF_CANCELED 0x8 +#define TIMER_INVALID_CPU ((unsigned int)-1) + #define TIMER_HTABLE_SIZE 2048 #if !ISP2(TIMER_HTABLE_SIZE) @@ -73,6 +76,9 @@ struct timer_bucket { * The hash table bucket matching the last time member has already been * processed, and the next periodic event resumes from the next bucket. * + * The cpu member is used to determine which lock serializes access to + * the structure. It must be accessed atomically. + * * Locking order: interrupts -> timer_cpu_data. */ struct timer_cpu_data { @@ -85,15 +91,43 @@ struct timer_cpu_data { static struct timer_cpu_data timer_cpu_data __percpu; static struct timer_cpu_data * -timer_lock_cpu_data(struct timer *timer, unsigned long *flagsp) +timer_cpu_data_acquire(unsigned long *flags) { struct timer_cpu_data *cpu_data; - cpu_data = percpu_ptr(timer_cpu_data, timer->cpu); - spinlock_lock_intr_save(&cpu_data->lock, flagsp); + thread_preempt_disable(); + cpu_data = cpu_local_ptr(timer_cpu_data); + spinlock_lock_intr_save(&cpu_data->lock, flags); + thread_preempt_enable_no_resched(); + return cpu_data; } +static struct timer_cpu_data * +timer_lock_cpu_data(struct timer *timer, unsigned long *flags) +{ + struct timer_cpu_data *cpu_data; + unsigned int cpu; + + for (;;) { + cpu = atomic_load(&timer->cpu, ATOMIC_RELAXED); + + if (cpu == TIMER_INVALID_CPU) { + return NULL; + } + + cpu_data = percpu_ptr(timer_cpu_data, cpu); + + spinlock_lock_intr_save(&cpu_data->lock, flags); + + if (cpu == atomic_load(&timer->cpu, ATOMIC_RELAXED)) { + return cpu_data; + } + + spinlock_unlock_intr_restore(&cpu_data->lock, *flags); + } +} + static void timer_unlock_cpu_data(struct timer_cpu_data *cpu_data, unsigned long flags) { @@ -125,7 +159,7 @@ timer_scheduled(const struct timer *timer) static void timer_set_scheduled(struct timer *timer, unsigned int cpu) { - timer->cpu = cpu; + atomic_store(&timer->cpu, cpu, ATOMIC_RELAXED); timer->state = TIMER_TS_SCHEDULED; } @@ -325,25 +359,6 @@ timer_cpu_data_init(struct timer_cpu_data *cpu_data, unsigned int cpu) } } -static struct timer_cpu_data * -timer_cpu_data_acquire_local(unsigned long *flags) -{ - struct timer_cpu_data *cpu_data; - - thread_pin(); - cpu_data = cpu_local_ptr(timer_cpu_data); - spinlock_lock_intr_save(&cpu_data->lock, flags); - return cpu_data; -} - -static void -timer_cpu_data_release_local(struct timer_cpu_data *cpu_data, - unsigned long flags) -{ - spinlock_unlock_intr_restore(&cpu_data->lock, flags); - thread_unpin(); -} - static struct timer_bucket * timer_cpu_data_get_bucket(struct timer_cpu_data *cpu_data, uint64_t ticks) { @@ -412,6 +427,7 @@ INIT_OP_DEFINE(timer_setup, void timer_init(struct timer *timer, timer_fn_t fn, int flags) { timer->fn = fn; + timer->cpu = TIMER_INVALID_CPU; timer->state = TIMER_TS_READY; timer->flags = 0; timer->joiner = NULL; @@ -433,18 +449,22 @@ timer_schedule(struct timer *timer, uint64_t ticks) struct timer_cpu_data *cpu_data; unsigned long cpu_flags; - cpu_data = timer_cpu_data_acquire_local(&cpu_flags); + cpu_data = timer_lock_cpu_data(timer, &cpu_flags); - if (timer_canceled(timer)) { - goto out; - } + if (cpu_data == NULL) { + cpu_data = timer_cpu_data_acquire(&cpu_flags); + } else { + if (timer_canceled(timer)) { + goto out; + } - /* - * If called from the handler, the timer is running. If rescheduled - * after completion, it's done. - */ - if (timer_running(timer) || timer_done(timer)) { - timer_set_ready(timer); + /* + * If called from the handler, the timer is running. If rescheduled + * after completion, it's done. + */ + if (timer_running(timer) || timer_done(timer)) { + timer_set_ready(timer); + } } timer_set_time(timer, ticks); @@ -457,7 +477,7 @@ timer_schedule(struct timer *timer, uint64_t ticks) timer_set_scheduled(timer, cpu_data->cpu); out: - timer_cpu_data_release_local(cpu_data, cpu_flags); + timer_unlock_cpu_data(cpu_data, cpu_flags); } void -- cgit v1.2.3 From 4e0b9efa25b58f7fcaf485349f66bd694390080a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 00:59:00 +0200 Subject: Makefile.am: do not force the -fno-stack-protector option Stack smashing protection has been supported for some time. Remove this option from the build system and let users pass what they want through CFLAGS. --- Makefile.am | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index de3e574..8f46ba1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,9 +32,6 @@ AM_CFLAGS += \ -fsigned-char \ -fno-common -# TODO Add option -AM_CFLAGS += -fno-stack-protector - AM_CFLAGS += -nostdlib SUFFIXES += .lds .lds.S -- cgit v1.2.3 From b9ec5cca6f6e3fd571b2e534d36e2498e488164c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 23:17:47 +0200 Subject: kern/timer: improve access synchronization --- kern/timer.c | 7 ++----- kern/timer.h | 3 ++- kern/timer_i.h | 26 ++++++++++++++++++++------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/kern/timer.c b/kern/timer.c index 365b2fa..ba148d5 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -76,9 +76,6 @@ struct timer_bucket { * The hash table bucket matching the last time member has already been * processed, and the next periodic event resumes from the next bucket. * - * The cpu member is used to determine which lock serializes access to - * the structure. It must be accessed atomically. - * * Locking order: interrupts -> timer_cpu_data. */ struct timer_cpu_data { @@ -242,13 +239,13 @@ timer_set_canceled(struct timer *timer) static void timer_set_time(struct timer *timer, uint64_t ticks) { - timer->ticks = ticks; + atomic_store(&timer->ticks, ticks, ATOMIC_RELAXED); } static bool timer_occurred(const struct timer *timer, uint64_t ref) { - return clock_time_occurred(timer->ticks, ref); + return clock_time_occurred(timer_get_time(timer), ref); } static uintptr_t diff --git a/kern/timer.h b/kern/timer.h index 4661668..a082c53 100644 --- a/kern/timer.h +++ b/kern/timer.h @@ -23,6 +23,7 @@ #include +#include #include /* @@ -47,7 +48,7 @@ typedef void (*timer_fn_t)(struct timer *); static inline uint64_t timer_get_time(const struct timer *timer) { - return timer->ticks; /* TODO atomic */ + return atomic_load(&timer->ticks, ATOMIC_RELAXED); } /* diff --git a/kern/timer_i.h b/kern/timer_i.h index 4ed01f2..40e97f1 100644 --- a/kern/timer_i.h +++ b/kern/timer_i.h @@ -24,18 +24,32 @@ #include #include +/* + * Locking keys : + * (c) cpu_data + * (a) atomic + * + * (*) The ticks member represents the expiration date. It may be read without + * locking the timer, in which case it must be accessed atomically. It + * may only be updated when the timer is locked though, so reads at such + * times don't need to be atomic. + * + * (**) The cpu member is used to determine which lock serializes access to + * the structure. It must be accessed atomically, but updated while the + * timer is locked. + */ struct timer { union { - struct hlist_node node; + struct hlist_node node; /* (c) */ struct work work; }; - uint64_t ticks; + uint64_t ticks; /* (c,a,*) */ timer_fn_t fn; - unsigned int cpu; - unsigned short state; - unsigned short flags; - struct thread *joiner; + unsigned int cpu; /* (c,a,**) */ + unsigned short state; /* (c) */ + unsigned short flags; /* (c) */ + struct thread *joiner; /* (c) */ }; #endif /* _KERN_TIMER_I_H */ -- cgit v1.2.3 From c04c1bdd385af886422b89e1bf751b9a9d322ffc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 23:17:47 +0200 Subject: Make mutex debugging slightly more convenient Define the macro controlling mutex debugging only if not already defined, allowing users to pass it through CFLAGS. --- kern/mutex/mutex_adaptive.c | 3 ++- kern/mutex/mutex_plain.c | 3 ++- kern/rtmutex.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c index ceec8f1..d5cab2f 100644 --- a/kern/mutex/mutex_adaptive.c +++ b/kern/mutex/mutex_adaptive.c @@ -31,8 +31,9 @@ #include #include -/* Set to 1 to enable debugging */ +#ifndef MUTEX_ADAPTIVE_DEBUG #define MUTEX_ADAPTIVE_DEBUG 0 +#endif /* MUTEX_ADAPTIVE_DEBUG */ #if MUTEX_ADAPTIVE_DEBUG diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c index 6a7cbdc..2c65594 100644 --- a/kern/mutex/mutex_plain.c +++ b/kern/mutex/mutex_plain.c @@ -27,8 +27,9 @@ #include #include -/* Set to 1 to enable debugging */ +#ifndef MUTEX_PLAIN_DEBUG #define MUTEX_PLAIN_DEBUG 0 +#endif /* MUTEX_PLAIN_DEBUG */ #if MUTEX_PLAIN_DEBUG diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 9fcc4e0..c07bbfe 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -29,8 +29,9 @@ #include #include -/* Set to 1 to enable debugging */ +#ifndef RTMUTEX_DEBUG #define RTMUTEX_DEBUG 0 +#endif /* RTMUTEX_DEBUG */ #if RTMUTEX_DEBUG -- cgit v1.2.3 From 406a8a1aeec188fde53050ea9b58fb2bcff9a2d0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 23:17:47 +0200 Subject: kern/mutex/mutex_adaptive: rename some debugging counters --- kern/mutex/mutex_adaptive.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c index d5cab2f..68b006a 100644 --- a/kern/mutex/mutex_adaptive.c +++ b/kern/mutex/mutex_adaptive.c @@ -43,8 +43,8 @@ enum { MUTEX_ADAPTIVE_SC_WAIT_ERRORS, MUTEX_ADAPTIVE_SC_DOWNGRADES, MUTEX_ADAPTIVE_SC_ERROR_DOWNGRADES, - MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS, MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT, + MUTEX_ADAPTIVE_SC_ERROR_RESET, MUTEX_ADAPTIVE_SC_FAST_UNLOCKS, MUTEX_ADAPTIVE_SC_SLOW_UNLOCKS, MUTEX_ADAPTIVE_SC_EXTERNAL_UNLOCKS, @@ -74,10 +74,10 @@ mutex_adaptive_setup_debug(void) "mutex_adaptive_downgrades"); mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_DOWNGRADES, "mutex_adaptive_error_downgrades"); - mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS, - "mutex_adaptive_error_clearbits"); mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT, "mutex_adaptive_error_clearcont"); + mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_ERROR_RESET, + "mutex_adaptive_error_reset"); mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_FAST_UNLOCKS, "mutex_adaptive_fast_unlocks"); mutex_adaptive_register_sc(MUTEX_ADAPTIVE_SC_SLOW_UNLOCKS, @@ -197,7 +197,7 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) /* If there is an owner, try to clear the contended bit */ if (thread != NULL) { - mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARBITS); + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT); owner = atomic_cas(&mutex->owner, owner, (uintptr_t)thread, ATOMIC_RELAXED); assert(owner & MUTEX_ADAPTIVE_CONTENDED); @@ -210,7 +210,7 @@ mutex_adaptive_lock_slow_common(struct mutex *mutex, bool timed, uint64_t ticks) * value of the mutex to become different from the contended bit. */ if (thread == NULL) { - mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_CLEARCONT); + mutex_adaptive_inc_sc(MUTEX_ADAPTIVE_SC_ERROR_RESET); owner = atomic_cas(&mutex->owner, owner, 0, ATOMIC_RELAXED); assert(owner == MUTEX_ADAPTIVE_CONTENDED); } -- cgit v1.2.3 From bf9ea21a402d3e006edce6d87ca7b0a16adbe3f0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 23:17:47 +0200 Subject: Make the low resolution clock frequency configurable --- configure.ac | 9 +++++++++ kern/clock.h | 6 ++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 947786c..20d8156 100644 --- a/configure.ac +++ b/configure.ac @@ -47,6 +47,12 @@ AC_ARG_WITH([max-cpus], [opt_max_cpus=$withval], [opt_max_cpus=128]) +AC_ARG_WITH([clock-freq], + [AS_HELP_STRING([--with-clock-freq=CLOCK_FREQ], + [set the low resolution clock frequency])], + [opt_clock_freq=$withval], + [opt_clock_freq=200]) + AC_ARG_ENABLE([mutex-adaptive], [AS_HELP_STRING([--enable-mutex-adaptive], [enable adaptive spinning mutexes])]) @@ -112,7 +118,10 @@ AM_CONDITIONAL([TEST_XCALL], AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], [maximum number of supported processors]) +AC_DEFINE_UNQUOTED([X15_CLOCK_FREQ], [$opt_clock_freq], + [low resolution clock frequency]) AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) +AC_MSG_NOTICE([low resolution clock frequency: $opt_clock_freq Hz]) AS_IF([test x"$enable_mutex_adaptive" = xyes -a x"$enable_mutex_pi" = xyes], [AC_MSG_ERROR([--enable-mutex-adaptive and --enable-mutex-pi are mutually exclusive])]) diff --git a/kern/clock.h b/kern/clock.h index 30db0a8..fa48a47 100644 --- a/kern/clock.h +++ b/kern/clock.h @@ -31,12 +31,10 @@ /* * Clock frequency. - * - * TODO Clock frequency selection. */ -#define CLOCK_FREQ 200 +#define CLOCK_FREQ X15_CLOCK_FREQ -#if (1000 % CLOCK_FREQ) != 0 +#if (CLOCK_FREQ < 100) || (CLOCK_FREQ > 1000) || (1000 % CLOCK_FREQ) != 0 #error "invalid clock frequency" #endif /* (1000 % CLOCK_FREQ) != 0 */ -- cgit v1.2.3 From fe715443e82b068a0b061f1b8ed3c01a96b1e4ba Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 31 Aug 2017 23:17:47 +0200 Subject: kern/thread: fix atomic accesses to a thread's run queue --- kern/thread.c | 12 ++++++++---- kern/thread_i.h | 17 +++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index e4d8f7c..4a9cb2a 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -492,7 +492,7 @@ thread_runq_add(struct thread_runq *runq, struct thread *thread) thread_set_flag(runq->current, THREAD_YIELD); } - thread->runq = runq; + atomic_store(&thread->runq, runq, ATOMIC_RELAXED); thread->in_runq = true; } @@ -1861,11 +1861,11 @@ thread_lock_runq(struct thread *thread, unsigned long *flags) struct thread_runq *runq; for (;;) { - runq = thread->runq; /* TODO Atomic access */ + runq = atomic_load(&thread->runq, ATOMIC_RELAXED); spinlock_lock_intr_save(&runq->lock, flags); - if (runq == thread->runq) { + if (runq == atomic_load(&thread->runq, ATOMIC_RELAXED)) { return runq; } @@ -2457,6 +2457,10 @@ thread_wakeup_common(struct thread *thread, int error) if (!thread->pinned) { runq = thread_get_real_sched_ops(thread)->select_runq(thread); } else { + /* + * This access doesn't need to be atomic, as the current thread is + * the only one which may update the member. + */ runq = thread->runq; spinlock_lock(&runq->lock); } @@ -2876,7 +2880,7 @@ thread_is_running(const struct thread *thread) { const struct thread_runq *runq; - runq = thread->runq; + runq = atomic_load(&thread->runq, ATOMIC_RELAXED); return (runq != NULL) && (atomic_load(&runq->current, ATOMIC_RELAXED) == thread); diff --git a/kern/thread_i.h b/kern/thread_i.h index 9ff4b76..4218b50 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -95,6 +95,11 @@ struct thread_fs_data { * (a) atomic * (-) thread-local * ( ) read-only + * + * (*) The runq member is used to determine which run queue lock must be + * held to serialize access to the relevant members. However, it is only + * updated while the associated run queue is locked. As a result, atomic + * reads are only necessary outside critical sections. */ struct thread { alignas(CPU_L1_SIZE) struct tcb tcb; /* (r) */ @@ -103,12 +108,12 @@ struct thread { unsigned long flags; /* (a) */ /* Sleep/wake-up synchronization members */ - struct thread_runq *runq; /* (r) */ - bool in_runq; /* (r) */ - const void *wchan_addr; /* (r) */ - const char *wchan_desc; /* (r) */ - int wakeup_error; /* (r) */ - unsigned short state; /* (r) */ + struct thread_runq *runq; /* (r,*) */ + bool in_runq; /* (r) */ + const void *wchan_addr; /* (r) */ + const char *wchan_desc; /* (r) */ + int wakeup_error; /* (r) */ + unsigned short state; /* (r) */ /* Sleep queue available for lending */ struct sleepq *priv_sleepq; /* (-) */ -- cgit v1.2.3 From 850c52ee1b4e91c6781d337bc129302697188f62 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 1 Sep 2017 23:58:40 +0200 Subject: kern/timer: make timer time reads non atomic This makes such accesses on 32-bits processor without 64-bits atomic instruction too cumbersome for what it's worth. --- kern/timer.c | 2 +- kern/timer.h | 6 ++++-- kern/timer_i.h | 17 ++++++----------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/kern/timer.c b/kern/timer.c index ba148d5..77a6bb3 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -239,7 +239,7 @@ timer_set_canceled(struct timer *timer) static void timer_set_time(struct timer *timer, uint64_t ticks) { - atomic_store(&timer->ticks, ticks, ATOMIC_RELAXED); + timer->ticks = ticks; } static bool diff --git a/kern/timer.h b/kern/timer.h index a082c53..ddace45 100644 --- a/kern/timer.h +++ b/kern/timer.h @@ -23,7 +23,6 @@ #include -#include #include /* @@ -44,11 +43,14 @@ typedef void (*timer_fn_t)(struct timer *); /* * Return the absolute expiration time of the timer, in ticks. + * + * This function may not be called while another thread is scheduling the + * timer. */ static inline uint64_t timer_get_time(const struct timer *timer) { - return atomic_load(&timer->ticks, ATOMIC_RELAXED); + return timer->ticks; } /* diff --git a/kern/timer_i.h b/kern/timer_i.h index 40e97f1..bbe07f9 100644 --- a/kern/timer_i.h +++ b/kern/timer_i.h @@ -29,12 +29,7 @@ * (c) cpu_data * (a) atomic * - * (*) The ticks member represents the expiration date. It may be read without - * locking the timer, in which case it must be accessed atomically. It - * may only be updated when the timer is locked though, so reads at such - * times don't need to be atomic. - * - * (**) The cpu member is used to determine which lock serializes access to + * (*) The cpu member is used to determine which lock serializes access to * the structure. It must be accessed atomically, but updated while the * timer is locked. */ @@ -44,12 +39,12 @@ struct timer { struct work work; }; - uint64_t ticks; /* (c,a,*) */ + uint64_t ticks; /* (c) */ timer_fn_t fn; - unsigned int cpu; /* (c,a,**) */ - unsigned short state; /* (c) */ - unsigned short flags; /* (c) */ - struct thread *joiner; /* (c) */ + unsigned int cpu; /* (c,a,*) */ + unsigned short state; /* (c) */ + unsigned short flags; /* (c) */ + struct thread *joiner; /* (c) */ }; #endif /* _KERN_TIMER_I_H */ -- cgit v1.2.3 From 897ad6a062ea2a32a2759613608faf3271211832 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 1 Sep 2017 23:58:40 +0200 Subject: kern/macros: new __unused macro --- kern/macros.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kern/macros.h b/kern/macros.h index 6b203e5..83bab87 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -98,4 +98,8 @@ moo_print(const char *s) #define __packed __attribute__((packed)) #endif +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + #endif /* _KERN_MACROS_H */ -- cgit v1.2.3 From d18d0e85596f90e0bd597b33d58209d0b3973c95 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 1 Sep 2017 23:58:41 +0200 Subject: Make assert have no side effects This makes sure symbols referenced by assert uses may not be generated if unused. The recently introduced __unused macro is used to suppress compiler warnings resulting from this change. --- arch/x86/machine/cga.c | 4 ++-- arch/x86/machine/cpu.c | 4 +++- arch/x86/machine/ioapic.c | 2 +- include/assert.h | 4 ++-- kern/cbuf.c | 2 +- kern/condition.c | 2 +- kern/kmem.c | 2 +- kern/log.c | 2 +- kern/mutex/mutex_adaptive.c | 3 ++- kern/mutex/mutex_plain.c | 3 ++- kern/rtmutex.c | 2 +- kern/semaphore.c | 3 ++- kern/sleepq.c | 29 +++++++++++++++-------------- kern/spinlock.h | 10 ++++++++-- kern/sref.c | 2 +- kern/task.h | 5 +++-- kern/thread.c | 27 ++++++++++++++------------- kern/thread.h | 3 ++- kern/timer.c | 2 +- kern/turnstile.c | 30 +++++++++++++++--------------- kern/turnstile.h | 8 ++++++-- vm/vm_kmem.c | 4 ++-- vm/vm_page.c | 2 +- vm/vm_page.h | 2 +- 24 files changed, 88 insertions(+), 69 deletions(-) diff --git a/arch/x86/machine/cga.c b/arch/x86/machine/cga.c index b519ba1..d086326 100644 --- a/arch/x86/machine/cga.c +++ b/arch/x86/machine/cga.c @@ -216,7 +216,7 @@ static void cga_bbuf_redraw(struct cga_bbuf *bbuf) { size_t size; - int error; + __unused int error; size = CGA_MEMORY_SIZE; error = cbuf_read(&bbuf->cbuf, bbuf->view, cga_memory, &size); @@ -298,7 +298,7 @@ cga_bbuf_newline(struct cga_bbuf *bbuf) { uint16_t cursor = 0, spaces[CGA_COLUMNS]; size_t i, nr_spaces, offset, size; - int error; + __unused int error; cga_bbuf_reset_view(bbuf); diff --git a/arch/x86/machine/cpu.c b/arch/x86/machine/cpu.c index 98d3680..e971d99 100644 --- a/arch/x86/machine/cpu.c +++ b/arch/x86/machine/cpu.c @@ -466,7 +466,9 @@ cpu_init(struct cpu *cpu) cpu->phys_addr_width = 0; cpu->virt_addr_width = 0; - assert(max_basic >= 1); + if (max_basic == 0) { + panic("cpu: unsupported maximum input value for basic information"); + } eax = 1; cpu_cpuid(&eax, &ebx, &ecx, &edx); diff --git a/arch/x86/machine/ioapic.c b/arch/x86/machine/ioapic.c index 82fbd6d..d70ec92 100644 --- a/arch/x86/machine/ioapic.c +++ b/arch/x86/machine/ioapic.c @@ -209,7 +209,7 @@ ioapic_create(unsigned int apic_id, uintptr_t addr, unsigned int gsi_base) return ioapic; } -static bool +__unused static bool ioapic_has_gsi(const struct ioapic *ioapic, unsigned int gsi) { return ((gsi >= ioapic->first_gsi) && (gsi <= ioapic->last_gsi)); diff --git a/include/assert.h b/include/assert.h index 8fdc2fe..9bf3bdf 100644 --- a/include/assert.h +++ b/include/assert.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Richard Braun. + * Copyright (c) 2010-2017 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 @@ -21,7 +21,7 @@ #define static_assert _Static_assert #ifdef NDEBUG -#define assert(expression) ((void)(expression)) +#define assert(expression) #else /* NDEBUG */ #include diff --git a/kern/cbuf.c b/kern/cbuf.c index 2093f42..eca0c33 100644 --- a/kern/cbuf.c +++ b/kern/cbuf.c @@ -72,7 +72,7 @@ cbuf_push(struct cbuf *cbuf, const void *buf, size_t size, bool erase) int cbuf_pop(struct cbuf *cbuf, void *buf, size_t *sizep) { - int error; + __unused int error; if (cbuf_size(cbuf) == 0) { return ERROR_AGAIN; diff --git a/kern/condition.c b/kern/condition.c index e6d6595..b12068f 100644 --- a/kern/condition.c +++ b/kern/condition.c @@ -88,7 +88,7 @@ condition_wait_common(struct condition *condition, struct mutex *mutex, void condition_wait(struct condition *condition, struct mutex *mutex) { - int error; + __unused int error; error = condition_wait_common(condition, mutex, false, 0); assert(!error); diff --git a/kern/kmem.c b/kern/kmem.c index d98be0d..5423c0a 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -646,7 +646,7 @@ kmem_cache_register(struct kmem_cache *cache, struct kmem_slab *slab) uintptr_t va, end; phys_addr_t pa; bool virtual; - int error; + __unused int error; assert(kmem_cache_registration_required(cache)); assert(slab->nr_refs == 0); diff --git a/kern/log.c b/kern/log.c index a48afc5..10131d1 100644 --- a/kern/log.c +++ b/kern/log.c @@ -459,7 +459,7 @@ INIT_OP_DEFINE(log_start, static void log_write(const void *s, size_t size) { - int error; + __unused int error; error = cbuf_push(&log_cbuf, s, size, true); assert(!error); diff --git a/kern/mutex/mutex_adaptive.c b/kern/mutex/mutex_adaptive.c index 68b006a..8da5527 100644 --- a/kern/mutex/mutex_adaptive.c +++ b/kern/mutex/mutex_adaptive.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -235,7 +236,7 @@ out: void mutex_adaptive_lock_slow(struct mutex *mutex) { - int error; + __unused int error; error = mutex_adaptive_lock_slow_common(mutex, false, 0); assert(!error); diff --git a/kern/mutex/mutex_plain.c b/kern/mutex/mutex_plain.c index 2c65594..abfa339 100644 --- a/kern/mutex/mutex_plain.c +++ b/kern/mutex/mutex_plain.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -133,7 +134,7 @@ out: void mutex_plain_lock_slow(struct mutex *mutex) { - int error; + __unused int error; error = mutex_plain_lock_slow_common(mutex, false, 0); assert(!error); diff --git a/kern/rtmutex.c b/kern/rtmutex.c index c07bbfe..0963751 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -183,7 +183,7 @@ out: void rtmutex_lock_slow(struct rtmutex *rtmutex) { - int error; + __unused int error; error = rtmutex_lock_slow_common(rtmutex, false, 0); assert(!error); diff --git a/kern/semaphore.c b/kern/semaphore.c index 72e843a..003f053 100644 --- a/kern/semaphore.c +++ b/kern/semaphore.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -63,7 +64,7 @@ semaphore_wait_slow_common(struct semaphore *semaphore, void semaphore_wait_slow(struct semaphore *semaphore) { - int error; + __unused int error; error = semaphore_wait_slow_common(semaphore, false, 0); assert(!error); diff --git a/kern/sleepq.c b/kern/sleepq.c index 170b1a9..0c20ae4 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -110,14 +110,14 @@ sleepq_waiter_wakeup(struct sleepq_waiter *waiter) thread_wakeup(waiter->thread); } -static void -sleepq_assert_init_state(const struct sleepq *sleepq) +__unused static bool +sleepq_state_initialized(const struct sleepq *sleepq) { - assert(sleepq->bucket == NULL); - assert(sleepq->sync_obj == NULL); - assert(list_empty(&sleepq->waiters)); - assert(sleepq->oldest_waiter == NULL); - assert(sleepq->next_free == NULL); + return ((sleepq->bucket == NULL) + && (sleepq->sync_obj == NULL) + && (list_empty(&sleepq->waiters)) + && (sleepq->oldest_waiter == NULL) + && (sleepq->next_free == NULL)); } static void @@ -134,7 +134,7 @@ sleepq_unuse(struct sleepq *sleepq) sleepq->sync_obj = NULL; } -static bool +__unused static bool sleepq_in_use(const struct sleepq *sleepq) { return sleepq->sync_obj != NULL; @@ -186,7 +186,8 @@ sleepq_bucket_add(struct sleepq_bucket *bucket, struct sleepq *sleepq) } static void -sleepq_bucket_remove(struct sleepq_bucket *bucket, struct sleepq *sleepq) +sleepq_bucket_remove(__unused struct sleepq_bucket *bucket, + struct sleepq *sleepq) { assert(sleepq->bucket == bucket); sleepq->bucket = NULL; @@ -253,14 +254,14 @@ sleepq_create(void) return NULL; } - sleepq_assert_init_state(sleepq); + assert(sleepq_state_initialized(sleepq)); return sleepq; } void sleepq_destroy(struct sleepq *sleepq) { - sleepq_assert_init_state(sleepq); + assert(sleepq_state_initialized(sleepq)); kmem_cache_free(&sleepq_cache, sleepq); } @@ -352,7 +353,7 @@ sleepq_lend(const void *sync_obj, bool condition, unsigned long *flags) assert(sync_obj != NULL); sleepq = thread_sleepq_lend(); - sleepq_assert_init_state(sleepq); + assert(sleepq_state_initialized(sleepq)); bucket = sleepq_bucket_get(sync_obj, condition); @@ -390,7 +391,7 @@ sleepq_return(struct sleepq *sleepq, unsigned long flags) spinlock_unlock_intr_restore(&bucket->lock, flags); - sleepq_assert_init_state(free_sleepq); + assert(sleepq_state_initialized(free_sleepq)); thread_sleepq_return(free_sleepq); } @@ -468,7 +469,7 @@ sleepq_wait_common(struct sleepq *sleepq, const char *wchan, void sleepq_wait(struct sleepq *sleepq, const char *wchan) { - int error; + __unused int error; error = sleepq_wait_common(sleepq, wchan, false, 0); assert(!error); diff --git a/kern/spinlock.h b/kern/spinlock.h index d4105da..50ac540 100644 --- a/kern/spinlock.h +++ b/kern/spinlock.h @@ -26,6 +26,8 @@ #ifndef _KERN_SPINLOCK_H #define _KERN_SPINLOCK_H +#include + #include #include #include @@ -35,13 +37,17 @@ struct spinlock; -#define spinlock_assert_locked(lock) assert((lock)->value != SPINLOCK_UNLOCKED) - /* * Initialize a spin lock. */ void spinlock_init(struct spinlock *lock); +static inline bool +spinlock_locked(const struct spinlock *lock) +{ + return lock->value != SPINLOCK_UNLOCKED; +} + /* * Attempt to lock the given spin lock. * diff --git a/kern/sref.c b/kern/sref.c index 3603e78..95daeb0 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -242,7 +242,7 @@ sref_queue_concat(struct sref_queue *queue1, struct sref_queue *queue2) queue1->size += queue2->size; } -static inline bool +__unused static inline bool sref_counter_aligned(const struct sref_counter *counter) { return (((uintptr_t)counter & (~SREF_WEAKREF_MASK)) == 0); diff --git a/kern/task.h b/kern/task.h index 1711fbe..149ff49 100644 --- a/kern/task.h +++ b/kern/task.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Richard Braun. + * Copyright (c) 2012-2017 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +51,7 @@ extern struct task *kernel_task; static inline void task_ref(struct task *task) { - unsigned long nr_refs; + __unused unsigned long nr_refs; nr_refs = atomic_fetch_add(&task->nr_refs, 1, ATOMIC_RELAXED); assert(nr_refs != (unsigned long)-1); diff --git a/kern/thread.c b/kern/thread.c index 4a9cb2a..5d8ac11 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -475,7 +475,7 @@ thread_runq_add(struct thread_runq *runq, struct thread *thread) const struct thread_sched_ops *ops; assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); assert(!thread->in_runq); ops = thread_get_real_sched_ops(thread); @@ -502,7 +502,7 @@ thread_runq_remove(struct thread_runq *runq, struct thread *thread) const struct thread_sched_ops *ops; assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); assert(thread->in_runq); runq->nr_threads--; @@ -523,7 +523,7 @@ thread_runq_put_prev(struct thread_runq *runq, struct thread *thread) const struct thread_sched_ops *ops; assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); ops = thread_get_real_sched_ops(thread); @@ -539,7 +539,7 @@ thread_runq_get_next(struct thread_runq *runq) unsigned int i; assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); for (i = 0; i < ARRAY_SIZE(thread_sched_ops); i++) { thread = thread_sched_ops[i].get_next(runq); @@ -572,7 +572,7 @@ static void thread_runq_wakeup(struct thread_runq *runq, struct thread *thread) { assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); assert(thread->state == THREAD_RUNNING); thread_runq_add(runq, thread); @@ -612,7 +612,7 @@ thread_runq_schedule(struct thread_runq *runq) && (__builtin_frame_address(0) < (prev->stack + TCB_STACK_SIZE))); assert(prev->preempt == THREAD_SUSPEND_PREEMPT_LEVEL); assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); llsync_report_context_switch(); @@ -657,7 +657,7 @@ thread_runq_schedule(struct thread_runq *runq) assert(prev->preempt == THREAD_SUSPEND_PREEMPT_LEVEL); assert(!cpu_intr_enabled()); - spinlock_assert_locked(&runq->lock); + assert(spinlock_locked(&runq->lock)); return runq; } @@ -762,7 +762,8 @@ thread_sched_rt_get_next(struct thread_runq *runq) } static void -thread_sched_rt_reset_priority(struct thread *thread, unsigned short priority) +thread_sched_rt_reset_priority(struct thread *thread, + __unused unsigned short priority) { assert(priority <= THREAD_SCHED_RT_PRIO_MAX); thread->rt_data.time_slice = THREAD_DEFAULT_RR_TIME_SLICE; @@ -1888,12 +1889,12 @@ thread_unlock_runq(struct thread_runq *runq, unsigned long flags) static void * thread_alloc_stack(void) { - struct vm_page *first_page, *last_page; + __unused struct vm_page *first_page, *last_page; phys_addr_t first_pa, last_pa; size_t stack_size; uintptr_t va; void *mem; - int error; + __unused int error; stack_size = vm_page_round(TCB_STACK_SIZE); mem = vm_kmem_alloc((PAGE_SIZE * 2) + stack_size); @@ -2546,7 +2547,7 @@ void thread_sleep(struct spinlock *interlock, const void *wchan_addr, const char *wchan_desc) { - int error; + __unused int error; error = thread_sleep_common(interlock, wchan_addr, wchan_desc, false, 0); assert(!error); @@ -2776,14 +2777,14 @@ thread_pi_setscheduler(struct thread *thread, unsigned char policy, unsigned short priority) { const struct thread_sched_ops *ops; + __unused struct turnstile_td *td; struct thread_runq *runq; - struct turnstile_td *td; unsigned int global_priority; unsigned long flags; bool requeue, current; td = thread_turnstile_td(thread); - turnstile_td_assert_lock(td); + assert(turnstile_td_locked(td)); ops = thread_get_sched_ops(thread_policy_to_class(policy)); global_priority = ops->get_global_priority(priority); diff --git a/kern/thread.h b/kern/thread.h index a3f2670..1a81168 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -281,7 +282,7 @@ void thread_pi_setscheduler(struct thread *thread, unsigned char policy, static inline void thread_ref(struct thread *thread) { - unsigned long nr_refs; + __unused unsigned long nr_refs; nr_refs = atomic_fetch_add(&thread->nr_refs, 1, ATOMIC_RELAXED); assert(nr_refs != (unsigned long)-1); diff --git a/kern/timer.c b/kern/timer.c index 77a6bb3..57f1765 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -135,7 +135,7 @@ timer_unlock_cpu_data(struct timer_cpu_data *cpu_data, unsigned long flags) * Timer state functions. */ -static bool +__unused static bool timer_ready(const struct timer *timer) { return timer->state == TIMER_TS_READY; diff --git a/kern/turnstile.c b/kern/turnstile.c index e59a7f3..a9b994d 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -401,14 +401,14 @@ turnstile_td_propagate_priority(struct turnstile_td *td) turnstile_td_propagate_priority_loop(td); } -static void -turnstile_assert_init_state(const struct turnstile *turnstile) +__unused static bool +turnstile_state_initialized(const struct turnstile *turnstile) { - assert(turnstile->bucket == NULL); - assert(turnstile->sync_obj == NULL); - assert(plist_empty(&turnstile->waiters)); - assert(turnstile->next_free == NULL); - assert(turnstile->top_waiter == NULL); + return ((turnstile->bucket == NULL) + && (turnstile->sync_obj == NULL) + && (plist_empty(&turnstile->waiters)) + && (turnstile->next_free == NULL) + && (turnstile->top_waiter == NULL)); } static void @@ -425,7 +425,7 @@ turnstile_unuse(struct turnstile *turnstile) turnstile->sync_obj = NULL; } -static bool +__unused static bool turnstile_in_use(const struct turnstile *turnstile) { return turnstile->sync_obj != NULL; @@ -464,7 +464,7 @@ turnstile_bucket_add(struct turnstile_bucket *bucket, } static void -turnstile_bucket_remove(struct turnstile_bucket *bucket, +turnstile_bucket_remove(__unused struct turnstile_bucket *bucket, struct turnstile *turnstile) { assert(turnstile->bucket == bucket); @@ -529,14 +529,14 @@ turnstile_create(void) return NULL; } - turnstile_assert_init_state(turnstile); + assert(turnstile_state_initialized(turnstile)); return turnstile; } void turnstile_destroy(struct turnstile *turnstile) { - turnstile_assert_init_state(turnstile); + assert(turnstile_state_initialized(turnstile)); kmem_cache_free(&turnstile_cache, turnstile); } @@ -603,7 +603,7 @@ turnstile_lend(const void *sync_obj) assert(sync_obj != NULL); turnstile = thread_turnstile_lend(); - turnstile_assert_init_state(turnstile); + assert(turnstile_state_initialized(turnstile)); td = thread_turnstile_td(thread_self()); bucket = turnstile_bucket_get(sync_obj); @@ -653,7 +653,7 @@ turnstile_return(struct turnstile *turnstile) spinlock_unlock(&bucket->lock); - turnstile_assert_init_state(free_turnstile); + assert(turnstile_state_initialized(free_turnstile)); thread_turnstile_return(free_turnstile); } @@ -774,7 +774,7 @@ void turnstile_wait(struct turnstile *turnstile, const char *wchan, struct thread *owner) { - int error; + __unused int error; error = turnstile_wait_common(turnstile, wchan, owner, false, 0); assert(!error); @@ -806,7 +806,7 @@ turnstile_own(struct turnstile *turnstile) { struct turnstile_td *td; struct thread *owner; - unsigned int top_priority; + __unused unsigned int top_priority; assert(turnstile->owner == NULL); diff --git a/kern/turnstile.h b/kern/turnstile.h index e7b4a5e..f7ba084 100644 --- a/kern/turnstile.h +++ b/kern/turnstile.h @@ -43,8 +43,6 @@ struct turnstile; */ struct turnstile_td; -#define turnstile_td_assert_lock(td) spinlock_assert_locked(&(td)->lock) - /* * Initialize turnstile thread data. */ @@ -62,6 +60,12 @@ turnstile_td_init(struct turnstile_td *td) * Turnstile thread data locking functions. */ +static inline bool +turnstile_td_locked(struct turnstile_td *td) +{ + return spinlock_locked(&(td)->lock); +} + static inline void turnstile_td_lock(struct turnstile_td *td) { diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 101c981..d526804 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -64,7 +64,7 @@ INIT_OP_DEFINE(vm_kmem_setup, INIT_OP_DEP(vm_object_setup, true), INIT_OP_DEP(vm_page_setup, true)); -static int +__unused static int vm_kmem_alloc_check(size_t size) { if (!vm_page_aligned(size) @@ -75,7 +75,7 @@ vm_kmem_alloc_check(size_t size) return 0; } -static int +__unused static int vm_kmem_free_check(uintptr_t va, size_t size) { if (!vm_page_aligned(va)) { diff --git a/vm/vm_page.c b/vm/vm_page.c index 49c2c11..19f6f4b 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -790,7 +790,7 @@ vm_page_lookup(phys_addr_t pa) return NULL; } -static bool +__unused static bool vm_page_block_referenced(const struct vm_page *page, unsigned int order) { unsigned int i, nr_pages; diff --git a/vm/vm_page.h b/vm/vm_page.h index 70a0091..e9ca321 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -240,7 +240,7 @@ vm_page_referenced(const struct vm_page *page) static inline void vm_page_ref(struct vm_page *page) { - unsigned int nr_refs; + __unused unsigned int nr_refs; nr_refs = atomic_fetch_add(&page->nr_refs, 1, ATOMIC_RELAXED); assert(nr_refs != (unsigned int)-1); -- cgit v1.2.3 From 1858793bd9a92aa1021fbc609557cf1c9c2c7270 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 09:58:51 +0200 Subject: doc/style(9): only mention C11 as the C specification in use --- doc/style.9.txt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index e479c8c..846a3e4 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -62,12 +62,11 @@ LANGUAGES --------- The kernel is mostly written in the C programming language, conforming -to ISO/IEC 9899:1999 (C99) and slowly transitioning to ISO/IEC 9899:2011 -(C11), with GNU extensions. While many GNU extensions are used, nested -functions are forbidden. They are considered too specific to the GCC -compiler. Note that X15 can currently be built with both GCC and Clang, -and the latter has no support for nested functions. Also note that the -kernel only expects the freestanding C environment from the compiler. +to ISO/IEC 9899:2011 (C11), with GNU extensions. While many GNU extensions +are used, nested functions are forbidden. They are considered too specific +to the GCC compiler. Note that X15 can currently be built with both GCC +and Clang, and the latter has no support for nested functions. Also note +that the kernel only expects the freestanding C environment from the compiler. Since X15 is low level system software, processor-specific assembly is inevitable. Developers should always refrain from writing assembly code -- cgit v1.2.3 From aa71571e6fde1f6a3a39f8ea48bcd940bde6fd28 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 10:02:16 +0200 Subject: kern/{h,s}list: add type declarations to public headers This change merely documents the types as part of the public interface of the respective modules. --- kern/hlist.h | 4 ++++ kern/slist.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/kern/hlist.h b/kern/hlist.h index eec05bb..e105cb8 100644 --- a/kern/hlist.h +++ b/kern/hlist.h @@ -28,6 +28,10 @@ #include #include +struct hlist; + +struct hlist_node; + /* * Static list initializer. */ diff --git a/kern/slist.h b/kern/slist.h index dd6a2d7..c0dcb08 100644 --- a/kern/slist.h +++ b/kern/slist.h @@ -28,6 +28,10 @@ #include #include +struct slist; + +struct slist_node; + /* * Static list initializer. */ -- cgit v1.2.3 From 36043bc14b2e99c363dde79a9231aeda13601041 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 15:33:59 +0200 Subject: kern/sleepq: use an hlist for hash table chaining --- kern/sleepq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kern/sleepq.c b/kern/sleepq.c index 0c20ae4..3a9273b 100644 --- a/kern/sleepq.c +++ b/kern/sleepq.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,7 @@ struct sleepq_bucket { alignas(CPU_L1_SIZE) struct spinlock lock; - struct list list; + struct hlist sleepqs; }; struct sleepq_waiter { @@ -53,7 +54,7 @@ struct sleepq_waiter { */ struct sleepq { alignas(CPU_L1_SIZE) struct sleepq_bucket *bucket; - struct list node; + struct hlist_node node; const void *sync_obj; struct list waiters; struct sleepq_waiter *oldest_waiter; @@ -150,7 +151,7 @@ static void sleepq_bucket_init(struct sleepq_bucket *bucket) { spinlock_init(&bucket->lock); - list_init(&bucket->list); + hlist_init(&bucket->sleepqs); } static struct sleepq_bucket * @@ -182,7 +183,7 @@ sleepq_bucket_add(struct sleepq_bucket *bucket, struct sleepq *sleepq) { assert(sleepq->bucket == NULL); sleepq->bucket = bucket; - list_insert_tail(&bucket->list, &sleepq->node); + hlist_insert_head(&bucket->sleepqs, &sleepq->node); } static void @@ -191,7 +192,7 @@ sleepq_bucket_remove(__unused struct sleepq_bucket *bucket, { assert(sleepq->bucket == bucket); sleepq->bucket = NULL; - list_remove(&sleepq->node); + hlist_remove(&sleepq->node); } static struct sleepq * @@ -199,7 +200,7 @@ sleepq_bucket_lookup(const struct sleepq_bucket *bucket, const void *sync_obj) { struct sleepq *sleepq; - list_for_each_entry(&bucket->list, sleepq, node) { + hlist_for_each_entry(&bucket->sleepqs, sleepq, node) { if (sleepq_in_use_by(sleepq, sync_obj)) { assert(sleepq->bucket == bucket); return sleepq; -- cgit v1.2.3 From 032b667c7b0855a9de72fe5ba62291e4493f58ce Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 15:42:41 +0200 Subject: kern/turnstile: use an hlist for hash table chaining --- kern/turnstile.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kern/turnstile.c b/kern/turnstile.c index a9b994d..3145399 100644 --- a/kern/turnstile.c +++ b/kern/turnstile.c @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -66,7 +67,7 @@ */ struct turnstile_bucket { alignas(CPU_L1_SIZE) struct spinlock lock; - struct list list; /* (b) */ + struct hlist turnstiles; /* (b) */ }; /* @@ -90,7 +91,7 @@ struct turnstile_bucket { */ struct turnstile { struct turnstile_bucket *bucket; /* (b*) */ - struct list node; /* (b) */ + struct hlist_node node; /* (b) */ const void *sync_obj; /* (b) */ struct plist waiters; /* (b,t) */ struct turnstile *next_free; /* (b) */ @@ -441,7 +442,7 @@ static void turnstile_bucket_init(struct turnstile_bucket *bucket) { spinlock_init(&bucket->lock); - list_init(&bucket->list); + hlist_init(&bucket->turnstiles); } static struct turnstile_bucket * @@ -460,7 +461,7 @@ turnstile_bucket_add(struct turnstile_bucket *bucket, { assert(turnstile->bucket == NULL); turnstile->bucket = bucket; - list_insert_tail(&bucket->list, &turnstile->node); + hlist_insert_head(&bucket->turnstiles, &turnstile->node); } static void @@ -469,7 +470,7 @@ turnstile_bucket_remove(__unused struct turnstile_bucket *bucket, { assert(turnstile->bucket == bucket); turnstile->bucket = NULL; - list_remove(&turnstile->node); + hlist_remove(&turnstile->node); } static struct turnstile * @@ -478,7 +479,7 @@ turnstile_bucket_lookup(const struct turnstile_bucket *bucket, { struct turnstile *turnstile; - list_for_each_entry(&bucket->list, turnstile, node) { + hlist_for_each_entry(&bucket->turnstiles, turnstile, node) { if (turnstile_in_use_by(turnstile, sync_obj)) { return turnstile; } -- cgit v1.2.3 From 1469ce26e2e58082fd4bbb343cad60f2d696a641 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 09:56:56 +0200 Subject: doc/style(9): fix boolean coercion section --- doc/style.9.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index 846a3e4..c4a5778 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -625,9 +625,8 @@ Boolean Coercion ~~~~~~~~~~~~~~~~ Boolean coercion should only be used when the resulting text is semantically -valid, i.e. it can be understood that the expression is either true, or false. - -checking error codes and pointers. For example : +valid, i.e. it can be understood that the expression is either true or false. +For example : [source,c] -------------------------------------------------------------------------------- -- cgit v1.2.3 From 7ded1b60d46d8582be0d86cd7176048bcfe30ccf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 16:06:54 +0200 Subject: kern/thread: new preemption control macros These new macros take care of disabling/restoring interrupts in the appropriate order. --- kern/llsync.c | 6 ++---- kern/spinlock.h | 13 ++++--------- kern/sref.c | 6 ++---- kern/thread.c | 18 ++++++------------ kern/thread.h | 26 ++++++++++++++++++++------ kern/work.c | 6 ++---- 6 files changed, 36 insertions(+), 39 deletions(-) diff --git a/kern/llsync.c b/kern/llsync.c index b4d09b5..34a3fa0 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -301,12 +301,10 @@ llsync_defer(struct work *work) struct llsync_cpu_data *cpu_data; unsigned long flags; - thread_preempt_disable(); - cpu_intr_save(&flags); + thread_preempt_disable_intr_save(&flags); cpu_data = llsync_get_cpu_data(); work_queue_push(&cpu_data->queue0, work); - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); } static void diff --git a/kern/spinlock.h b/kern/spinlock.h index 50ac540..49e6d55 100644 --- a/kern/spinlock.h +++ b/kern/spinlock.h @@ -33,7 +33,6 @@ #include #include #include -#include struct spinlock; @@ -121,13 +120,11 @@ spinlock_trylock_intr_save(struct spinlock *lock, unsigned long *flags) { int error; - thread_preempt_disable(); - cpu_intr_save(flags); + thread_preempt_disable_intr_save(flags); error = spinlock_lock_fast(lock); if (unlikely(error)) { - cpu_intr_restore(*flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(*flags); } return error; @@ -147,8 +144,7 @@ spinlock_trylock_intr_save(struct spinlock *lock, unsigned long *flags) static inline void spinlock_lock_intr_save(struct spinlock *lock, unsigned long *flags) { - thread_preempt_disable(); - cpu_intr_save(flags); + thread_preempt_disable_intr_save(flags); spinlock_lock_common(lock); } @@ -165,8 +161,7 @@ static inline void spinlock_unlock_intr_restore(struct spinlock *lock, unsigned long flags) { spinlock_unlock_common(lock); - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); } /* diff --git a/kern/sref.c b/kern/sref.c index 95daeb0..dd81ca6 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -798,15 +798,13 @@ sref_manage(void *arg) cache = arg; for (;;) { - thread_preempt_disable(); - cpu_intr_save(&flags); + thread_preempt_disable_intr_save(&flags); while (!sref_cache_is_dirty(cache)) { thread_sleep(NULL, cache, "sref"); } - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); sref_cache_flush(cache, &queue); sref_review(&queue); diff --git a/kern/thread.c b/kern/thread.c index 5d8ac11..9ee9b99 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1277,8 +1277,7 @@ thread_sched_fs_balance_scan(struct thread_runq *runq, remote_runq = NULL; - thread_preempt_disable(); - cpu_intr_save(&flags); + thread_preempt_disable_intr_save(&flags); cpumap_for_each(&thread_active_runqs, i) { tmp = percpu_ptr(thread_runq, i); @@ -1312,8 +1311,7 @@ thread_sched_fs_balance_scan(struct thread_runq *runq, spinlock_unlock(&remote_runq->lock); } - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); return remote_runq; } @@ -1444,8 +1442,7 @@ thread_sched_fs_balance(struct thread_runq *runq, unsigned long *flags) remote_runq = thread_sched_fs_balance_scan(runq, highest_round); if (remote_runq != NULL) { - thread_preempt_disable(); - cpu_intr_save(flags); + thread_preempt_disable_intr_save(flags); thread_runq_double_lock(runq, remote_runq); nr_migrations = thread_sched_fs_balance_migrate(runq, remote_runq, highest_round); @@ -1472,8 +1469,7 @@ thread_sched_fs_balance(struct thread_runq *runq, unsigned long *flags) continue; } - thread_preempt_disable(); - cpu_intr_save(flags); + thread_preempt_disable_intr_save(flags); thread_runq_double_lock(runq, remote_runq); nr_migrations = thread_sched_fs_balance_migrate(runq, remote_runq, highest_round); @@ -2452,8 +2448,7 @@ thread_wakeup_common(struct thread *thread, int error) thread_unlock_runq(runq, flags); } - thread_preempt_disable(); - cpu_intr_save(&flags); + thread_preempt_disable_intr_save(&flags); if (!thread->pinned) { runq = thread_get_real_sched_ops(thread)->select_runq(thread); @@ -2469,8 +2464,7 @@ thread_wakeup_common(struct thread *thread, int error) thread->wakeup_error = error; thread_runq_wakeup(runq, thread); spinlock_unlock(&runq->lock); - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); return 0; } diff --git a/kern/thread.h b/kern/thread.h index 1a81168..0797343 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -569,6 +569,17 @@ thread_preempt_enabled(void) return (thread_self()->preempt == 0); } +static inline void +thread_preempt_disable(void) +{ + struct thread *thread; + + thread = thread_self(); + thread->preempt++; + assert(thread->preempt != 0); + barrier(); +} + static inline void thread_preempt_enable_no_resched(void) { @@ -600,14 +611,17 @@ thread_preempt_enable(void) } static inline void -thread_preempt_disable(void) +thread_preempt_disable_intr_save(unsigned long *flags) { - struct thread *thread; + thread_preempt_disable(); + cpu_intr_save(flags); +} - thread = thread_self(); - thread->preempt++; - assert(thread->preempt != 0); - barrier(); +static inline void +thread_preempt_enable_intr_restore(unsigned long flags) +{ + cpu_intr_restore(flags); + thread_preempt_enable(); } /* diff --git a/kern/work.c b/kern/work.c index 4647522..c0eb0bb 100644 --- a/kern/work.c +++ b/kern/work.c @@ -217,8 +217,7 @@ work_pool_acquire(struct work_pool *pool, unsigned long *flags) if (pool->flags & WORK_PF_GLOBAL) { spinlock_lock_intr_save(&pool->lock, flags); } else { - thread_preempt_disable(); - cpu_intr_save(flags); + thread_preempt_disable_intr_save(flags); } } @@ -228,8 +227,7 @@ work_pool_release(struct work_pool *pool, unsigned long flags) if (pool->flags & WORK_PF_GLOBAL) { spinlock_unlock_intr_restore(&pool->lock, flags); } else { - cpu_intr_restore(flags); - thread_preempt_enable(); + thread_preempt_enable_intr_restore(flags); } } -- cgit v1.2.3 From ca45d38bbf535f1dd23df9b71f85cd9c820e0e69 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 16:47:27 +0200 Subject: Makefile.am: make x15.sorted_init_ops an intermediate file --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index 8f46ba1..efff4f3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -61,6 +61,8 @@ x15_SOURCES = x15_LDFLAGS = -nostdlib -Xlinker -T arch/$(arch)/x15.lds x15_LDADD = -lgcc +.INTERMEDIATE: x15.sorted_init_ops + include Makefrag.am x15.sorted_init_ops: $(filter %.c,$(x15_SOURCES)) -- cgit v1.2.3 From 608cf884efd9bb0ef6a308814fd22b1c8688fabf Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sat, 2 Sep 2017 23:03:40 +0200 Subject: Rework the build system --- INSTALL | 39 ++++------- Makefrag.am | 44 ++++++------ arch/x86/Makefrag.am | 14 ++-- arch/x86/configfrag.ac | 56 ++++++--------- arch/x86/machine/boot.c | 2 +- configure.ac | 182 ++++++++++++++++++++++++++++-------------------- kern/kmem.c | 4 +- kern/log.c | 4 +- kern/mutex.h | 14 ++-- kern/mutex_types.h | 10 +-- kern/shell.h | 6 +- kern/shutdown.c | 4 +- kern/syscnt.c | 4 +- kern/task.c | 4 +- kern/thread.c | 4 +- vm/vm_map.c | 4 +- vm/vm_page.c | 4 +- 17 files changed, 204 insertions(+), 195 deletions(-) diff --git a/INSTALL b/INSTALL index 6777e7a..4dec35a 100644 --- a/INSTALL +++ b/INSTALL @@ -236,6 +236,13 @@ an Autoconf bug. Until the bug is fixed you can use this workaround: X15 Options =========== +`--enable-64bits' + Build for a 64-bits target processor. + +`--enable-shell' + Enable the diagnostics shell. Note that some shell commands may have + a real-time unsafe behaviour. + `--enable-test-module=TEST_MODULE' Run a test module instead of booting the system. Test module source files can be found in the test directory, and test module names @@ -243,41 +250,25 @@ X15 Options in a file named `test_pmap_update_mp.c' would be selected with the option `--enable-test-module=pmap_update_mp'. -`--enable-mutex-adaptive' - Enable the adaptive spinning mutex implementation for regular mutexes. - Adaptive spinning mutexes optimistically spin instead of sleep if the - owner of a mutex is found running, in the hope the mutex is unlocked - soon. This optimization increases performance by avoiding some sleeps - and wakeups, but it makes the system globally non real-time. - -`--enable-mutex-pi' - Enable priority inheritance for regular mutexes (note that priority - inheritance is always enabled for real-time mutexes). This option is - likely to measurably reduce overall performance. It should only be - enabled if you know what you're doing. - -`--enable-shell' - Enable the diagnostics shell. Note that some shell commands may have - a real-time unsafe behaviour. - `--enable-thread-stack-guard' Enable the use of guard pages around kernel thread stacks to catch overflows. Note that this feature wastes precious kernel virtual memory and has some overhead during thread creation and destruction. +`--with-clock-freq=CLOCK_FREQ + Set the low resolution clock frequency. + `--with-max-cpus=MAX_CPUS' Set the maximum number of supported processors. +`--with-mutex-impl=MUTEX_IMPL' + Select the mutex implementation (adaptive, pi, or plain) + +TODO Improve descriptions x86 options ----------- -`--with-i386' - Build for a 32-bit IA-32 machine. - -`--with-amd64' - Build for a 64-bit AMD64 machine. - -`--enable-pae' +`--enable-x86-pae' Use the PAE (Physical Address Extension) processor feature to address physical memory beyond 4 GiB (i386 only). diff --git a/Makefrag.am b/Makefrag.am index d799bb8..2a91809 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -123,19 +123,19 @@ x15_SOURCES += \ kern/xcall.c \ kern/xcall.h -if MUTEX_ADAPTIVE +if USE_MUTEX_ADAPTIVE x15_SOURCES += kern/mutex/mutex_adaptive.c else -if !MUTEX_PI +if USE_MUTEX_PLAIN x15_SOURCES += kern/mutex/mutex_plain.c endif endif -if X15_SHELL +if ENABLE_SHELL x15_SOURCES += \ kern/shell.c \ kern/shell.h -endif X15_SHELL +endif ENABLE_SHELL x15_SOURCES += \ vm/vm_adv.h \ @@ -154,38 +154,38 @@ x15_SOURCES += \ x15_SOURCES += \ test/test.h -if TEST_LLSYNC_DEFER +if ENABLE_TEST_LLSYNC_DEFER x15_SOURCES += test/test_llsync_defer.c -endif TEST_LLSYNC_DEFER +endif ENABLE_TEST_LLSYNC_DEFER -if TEST_MUTEX +if ENABLE_TEST_MUTEX x15_SOURCES += test/test_mutex.c -endif TEST_MUTEX +endif ENABLE_TEST_MUTEX -if TEST_MUTEX_PI +if ENABLE_TEST_MUTEX_PI x15_SOURCES += test/test_mutex_pi.c -endif TEST_MUTEX_PI +endif ENABLE_TEST_MUTEX_PI -if TEST_PMAP_UPDATE_MP +if ENABLE_TEST_PMAP_UPDATE_MP x15_SOURCES += test/test_pmap_update_mp.c -endif TEST_PMAP_UPDATE_MP +endif ENABLE_TEST_PMAP_UPDATE_MP -if TEST_SREF_DIRTY_ZEROES +if ENABLE_TEST_SREF_DIRTY_ZEROES x15_SOURCES += test/test_sref_dirty_zeroes.c -endif TEST_SREF_DIRTY_ZEROES +endif ENABLE_TEST_SREF_DIRTY_ZEROES -if TEST_SREF_NOREF +if ENABLE_TEST_SREF_NOREF x15_SOURCES += test/test_sref_noref.c -endif TEST_SREF_NOREF +endif ENABLE_TEST_SREF_NOREF -if TEST_SREF_WEAKREF +if ENABLE_TEST_SREF_WEAKREF x15_SOURCES += test/test_sref_weakref.c -endif TEST_SREF_WEAKREF +endif ENABLE_TEST_SREF_WEAKREF -if TEST_VM_PAGE_FILL +if ENABLE_TEST_VM_PAGE_FILL x15_SOURCES += test/test_vm_page_fill.c -endif TEST_VM_PAGE_FILL +endif ENABLE_TEST_VM_PAGE_FILL -if TEST_XCALL +if ENABLE_TEST_XCALL x15_SOURCES += test/test_xcall.c -endif TEST_XCALL +endif ENABLE_TEST_XCALL diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am index cf53698..016e31c 100644 --- a/arch/x86/Makefrag.am +++ b/arch/x86/Makefrag.am @@ -12,16 +12,18 @@ AM_CFLAGS += -fno-omit-frame-pointer x15_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 -if I386 -AM_CPPFLAGS += -m32 -x15_LDFLAGS += -m32 -endif I386 +if X86_64 -if AMD64 AM_CPPFLAGS += -m64 AM_CFLAGS += -mcmodel=kernel -mno-red-zone x15_LDFLAGS += -m64 -endif AMD64 + +else !X86_64 + +AM_CPPFLAGS += -m32 +x15_LDFLAGS += -m32 + +endif !X86_64 x15_SOURCES += \ arch/x86/machine/acpi.c \ diff --git a/arch/x86/configfrag.ac b/arch/x86/configfrag.ac index 1d37a0e..0857368 100644 --- a/arch/x86/configfrag.ac +++ b/arch/x86/configfrag.ac @@ -2,43 +2,28 @@ m4_define([x86_ENABLE_PAE], [AC_DEFINE([X15_X86_PAE], [1], [use PAE page translation])]) m4_define([x86_SELECT_I386], - [machine=i386 - AS_IF([test x"$enable_pae" = xyes], [x86_ENABLE_PAE])]) + [subarch=i386 + AS_IF([test x"$enable_x86_pae" = xyes], [x86_ENABLE_PAE])]) m4_define([x86_SELECT_AMD64], - [machine=amd64 - AS_IF([test x"$enable_pae" = xyes], - [AC_MSG_WARN([pae option available for i386 only, ignoring])])]) + [subarch=amd64 + AS_IF([test x"$enable_x86_pae" = xyes], + [AC_MSG_WARN([pae option available for 32-bits builds only, ignoring])])]) m4_define([x86_SELECT], [arch=x86 - AC_ARG_WITH([i386], - [AS_HELP_STRING([--with-i386], - [build for a 32-bit IA-32 machine])], - [opt_i386=$withval], - [opt_i386=no]) - - AC_ARG_WITH([amd64], - [AS_HELP_STRING([--with-amd64], - [build for a 64-bit AMD64 machine])], - [opt_amd64=$withval], - [opt_amd64=no]) - - AC_ARG_ENABLE([pae], - [AS_HELP_STRING([--enable-pae], - [enable physical address extension (i386 only)])], + AC_ARG_ENABLE([x86-pae], + [AS_HELP_STRING([--enable-x86-pae], + [enable physical address extension (32-bits only)])], [], - [enable_pae=no]) + [enable_x86_pae=no]) - AS_IF([test x"$opt_i386$opt_amd64" = xyesyes], - [AC_MSG_ERROR([select only one of i386 or amd64])], - [test x"$opt_i386$opt_amd64" = xnono], + AS_IF([test -z "$enable_64bits"], [AS_CASE(["$host_cpu"], - [i?86], [opt_i386=yes], - [x86_64], [opt_amd64=yes])]) + [i?86], [enable_64bits=no], + [x86_64], [enable_64bits=yes])]) - AS_IF([test x"$opt_i386" = xyes], [x86_SELECT_I386], - [test x"$opt_amd64" = xyes], [x86_SELECT_AMD64]) + AS_IF([test x"$enable_64bits" = xyes], [x86_SELECT_AMD64], [x86_SELECT_I386]) # Never generate instructions that are unhandled in kernel mode AX_APPEND_COMPILE_FLAGS([ \ @@ -48,14 +33,15 @@ m4_define([x86_SELECT], -mno-sse2 \ -mno-avx]) - AC_DEFINE_UNQUOTED([X15_X86_MACHINE], [$machine], [machine]) - AC_MSG_NOTICE([machine type: $arch ($machine)]) - AS_IF([test x"$machine" = xi386], - [AC_MSG_NOTICE([physical address extension: $enable_pae])])]) + AC_DEFINE_UNQUOTED([X15_X86_SUBARCH], [$subarch], [subarch]) + + AS_ECHO + AC_MSG_NOTICE([target: $arch ($subarch)]) + AS_IF([test x"$subarch" = xi386], + [AC_MSG_NOTICE([x86: physical address extension: $enable_x86_pae])])]) AS_CASE(["$host_cpu"], [i?86|x86_64], [x86_SELECT]) -AM_CONDITIONAL([X86], [test "$arch" = x86]) -AM_CONDITIONAL([I386], [test x"$opt_i386" = xyes]) -AM_CONDITIONAL([AMD64], [test x"$opt_amd64" = xyes]) +AM_CONDITIONAL([X86], [test x"$arch" = xx86]) +AM_CONDITIONAL([X86_64], [test x"$arch" = xx86 -a x"$enable_64bits" = xyes]) diff --git a/arch/x86/machine/boot.c b/arch/x86/machine/boot.c index 82b5330..47055c8 100644 --- a/arch/x86/machine/boot.c +++ b/arch/x86/machine/boot.c @@ -353,7 +353,7 @@ boot_setup_paging(struct multiboot_raw_info *mbi, unsigned long eax) void __init boot_log_info(void) { - log_info(KERNEL_NAME "/" QUOTE(X15_X86_MACHINE) " " KERNEL_VERSION + log_info(KERNEL_NAME "/" QUOTE(X15_X86_SUBARCH) " " KERNEL_VERSION #ifdef X15_X86_PAE " PAE" #endif /* X15_X86_PAE */ diff --git a/configure.ac b/configure.ac index 20d8156..6077696 100644 --- a/configure.ac +++ b/configure.ac @@ -30,10 +30,19 @@ AX_APPEND_COMPILE_FLAGS([-no-pie -fno-pie]) AC_HEADER_ASSERT() -m4_include([arch/x86/configfrag.ac]) +# --enable options -AS_IF([test x"$arch" = x], [AC_MSG_ERROR([unsupported architecture])]) -AC_SUBST([arch]) +AC_ARG_ENABLE([64bits], + [AS_HELP_STRING([--enable-64bits], + [build for a 64-bits target processor])], + [], + [enable_64bits=default]) + +AC_ARG_ENABLE([shell], + [AS_HELP_STRING([--enable-shell], + [enable the diagnostics shell])], + [], + [enable_shell=no]) AC_ARG_ENABLE([test-module], [AS_HELP_STRING([--enable-test-module=TEST_MODULE], @@ -41,44 +50,56 @@ AC_ARG_ENABLE([test-module], [enable_test_module=$enableval], [enable_test_module=no]) +AC_ARG_ENABLE([thread-stack-guard], + [AS_HELP_STRING([--enable-thread-stack-guard], + [enable kernel thread stack guard pages])], + [], + [enable_thread_stack_guard=no]) + +# --with options + +AC_ARG_WITH([clock-freq], + [AS_HELP_STRING([--with-clock-freq=CLOCK_FREQ], + [set the low resolution clock frequency (100-1000 Hz)])], + [opt_clock_freq=$withval], + [opt_clock_freq=200]) + AC_ARG_WITH([max-cpus], [AS_HELP_STRING([--with-max-cpus=MAX_CPUS], [set the maximum number of supported processors])], [opt_max_cpus=$withval], [opt_max_cpus=128]) -AC_ARG_WITH([clock-freq], - [AS_HELP_STRING([--with-clock-freq=CLOCK_FREQ], - [set the low resolution clock frequency])], - [opt_clock_freq=$withval], - [opt_clock_freq=200]) +AC_ARG_WITH([mutex-impl], + [AS_HELP_STRING([--with-mutex-impl=MUTEX_IMPL], + [select the mutex implementation (adaptive, pi, or plain)])], + [opt_mutex_impl=$withval], + [opt_mutex_impl=plain]) -AC_ARG_ENABLE([mutex-adaptive], - [AS_HELP_STRING([--enable-mutex-adaptive], - [enable adaptive spinning mutexes])]) +# 32/64-bits processor selection -AC_ARG_ENABLE([mutex-pi], - [AS_HELP_STRING([--enable-mutex-pi], - [enable priority inheritance for regular mutexes - (note that priority inheritance is always - enabled for real-time mutexes)])], - [], - [enable_mutex_pi=no]) +AS_IF([test -z "$enable_64bits"], [enable_64bits=yes], + [test x"$enable_64bits" = x"default"], [enable_64bits=], + [test x"$enable_64bits" != xyes -a x"$enable_64bits" != xno], + [AC_MSG_ERROR([target processor must be either 32-bits or 64-bits])]) -AC_ARG_ENABLE([shell], - [AS_HELP_STRING([--enable-shell], - [enable the diagnostics shell])], - [], - [enable_shell=no]) +# Architecture-specific configuration -AC_ARG_ENABLE([thread-stack-guard], - [AS_HELP_STRING([--enable-thread-stack-guard], - [enable kernel thread stack guard pages])], - [], - [enable_thread_stack_guard=no]) +m4_include([arch/x86/configfrag.ac]) -AC_DEFINE([__KERNEL__], [1], [kernel code]) -AC_DEFINE_UNQUOTED([X15_ARCH], [$arch], [arch]) +AS_IF([test x"$arch" = x], [AC_MSG_ERROR([unsupported architecture])]) +AC_SUBST([arch]) + +# Shell selection + +AS_IF([test x"$enable_shell" = xyes], + [AC_DEFINE_UNQUOTED([X15_ENABLE_SHELL], [], + [Enable the diagnostics shell])]) +AM_CONDITIONAL([ENABLE_SHELL], + [test x"$enable_shell" = xyes]) +AC_MSG_NOTICE([diagnostics shell: $enable_shell]) + +# Test module selection m4_define([ENABLE_TEST_MODULE], [AS_CASE(["$enable_test_module"], @@ -92,70 +113,79 @@ m4_define([ENABLE_TEST_MODULE], [vm_page_fill], [test_vm_page_fill=yes], [xcall], [test_xcall=yes], [AC_MSG_ERROR([invalid test module])]) - AC_DEFINE([X15_RUN_TEST_MODULE], [1], - [run test module instead of booting]) + AC_DEFINE_UNQUOTED([X15_RUN_TEST_MODULE], [], + [run test module instead of booting]) AC_MSG_NOTICE([test module enabled: $enable_test_module])]) AS_IF([test x"$enable_test_module" != xno], [ENABLE_TEST_MODULE]) -AM_CONDITIONAL([TEST_LLSYNC_DEFER], +AM_CONDITIONAL([ENABLE_TEST_LLSYNC_DEFER], [test x"$test_llsync_defer" = xyes]) -AM_CONDITIONAL([TEST_MUTEX], +AM_CONDITIONAL([ENABLE_TEST_MUTEX], [test x"$test_mutex" = xyes]) -AM_CONDITIONAL([TEST_MUTEX_PI], +AM_CONDITIONAL([ENABLE_TEST_MUTEX_PI], [test x"$test_mutex_pi" = xyes]) -AM_CONDITIONAL([TEST_PMAP_UPDATE_MP], +AM_CONDITIONAL([ENABLE_TEST_PMAP_UPDATE_MP], [test x"$test_pmap_update_mp" = xyes]) -AM_CONDITIONAL([TEST_SREF_DIRTY_ZEROES], +AM_CONDITIONAL([ENABLE_TEST_SREF_DIRTY_ZEROES], [test x"$test_sref_dirty_zeroes" = xyes]) -AM_CONDITIONAL([TEST_SREF_NOREF], +AM_CONDITIONAL([ENABLE_TEST_SREF_NOREF], [test x"$test_sref_noref" = xyes]) -AM_CONDITIONAL([TEST_SREF_WEAKREF], +AM_CONDITIONAL([ENABLE_TEST_SREF_WEAKREF], [test x"$test_sref_weakref" = xyes]) -AM_CONDITIONAL([TEST_VM_PAGE_FILL], +AM_CONDITIONAL([ENABLE_TEST_VM_PAGE_FILL], [test x"$test_vm_page_fill" = xyes]) -AM_CONDITIONAL([TEST_XCALL], +AM_CONDITIONAL([ENABLE_TEST_XCALL], [test x"$test_xcall" = xyes]) -AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], - [maximum number of supported processors]) +# Thread stack guard selection + +AS_IF([test x"$enable_thread_stack_guard" = xyes], + [AC_DEFINE_UNQUOTED([X15_ENABLE_THREAD_STACK_GUARD], [], + [Enable the use of guard pages for thread stacks])]) +AC_MSG_NOTICE([thread stack guard pages: $enable_thread_stack_guard]) + +# Low resolution clock frequency + AC_DEFINE_UNQUOTED([X15_CLOCK_FREQ], [$opt_clock_freq], [low resolution clock frequency]) -AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) AC_MSG_NOTICE([low resolution clock frequency: $opt_clock_freq Hz]) -AS_IF([test x"$enable_mutex_adaptive" = xyes -a x"$enable_mutex_pi" = xyes], - [AC_MSG_ERROR([--enable-mutex-adaptive and --enable-mutex-pi are mutually exclusive])]) - -AS_IF([test x"$enable_mutex_adaptive" = xyes], - [mutex_impl="adaptive spinning"], - [test x"$enable_mutex_pi" = xyes], - [mutex_impl="priority inheritance"], - [mutex_impl=plain]) -AC_MSG_NOTICE([mutex implementation: $mutex_impl]) - -AS_IF([test x"$enable_mutex_adaptive" = xyes], - [AC_DEFINE_UNQUOTED([X15_MUTEX_ADAPTIVE], [], - [Enable adaptive mutexes])]) -AM_CONDITIONAL([MUTEX_ADAPTIVE], - [test x"$enable_mutex_adaptive" = xyes]) - -AS_IF([test x"$enable_mutex_pi" = xyes], - [AC_DEFINE_UNQUOTED([X15_MUTEX_PI], [], - [Enable priority inheritance for regular mutexes])]) -AM_CONDITIONAL([MUTEX_PI], - [test x"$enable_mutex_pi" = xyes]) - -AM_CONDITIONAL([X15_SHELL], - [test x"$enable_shell" = xyes]) -AS_IF([test x"$enable_shell" = xyes], - [AC_DEFINE_UNQUOTED([X15_SHELL], [], - [Enable the diagnostics shell])]) -AC_MSG_NOTICE([diagnostics shell: $enable_shell]) +# Maximum number of supported processors -AS_IF([test x"$enable_thread_stack_guard" = xyes], - [AC_DEFINE_UNQUOTED([X15_THREAD_STACK_GUARD], [], - [Enable the use of guard pages for thread stacks])]) -AC_MSG_NOTICE([thread stack guard pages: $enable_thread_stack_guard]) +AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], + [maximum number of supported processors]) +AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) + +# Mutex implementation selection + +m4_define([SELECT_MUTEX_IMPL], + [mutex_impl_desc="$2" + AC_DEFINE_UNQUOTED([$1], [], [$3])]) + +AS_IF([test x"$opt_mutex_impl" = xadaptive], + [SELECT_MUTEX_IMPL([X15_USE_MUTEX_ADAPTIVE], + [adaptive spinning], + [Use adaptive spinning mutex implementation])], + [test x"$opt_mutex_impl" = xpi], + [SELECT_MUTEX_IMPL([X15_USE_MUTEX_PI], + [priority inheritance], + [Use priority inheritance mutex implementation])], + [test x"$opt_mutex_impl" = xplain], + [SELECT_MUTEX_IMPL([X15_USE_MUTEX_PLAIN], + [plain], + [Use plain mutex implementation])], + [AC_MSG_ERROR([invalid mutex implementation])]) +AM_CONDITIONAL([USE_MUTEX_ADAPTIVE], + [test x"$opt_mutex_impl" = xadaptive]) +AM_CONDITIONAL([USE_MUTEX_PI], + [test x"$opt_mutex_impl" = xpi]) +AM_CONDITIONAL([USE_MUTEX_PLAIN], + [test x"$opt_mutex_impl" = xplain]) +AC_MSG_NOTICE([mutex implementation: $mutex_impl_desc]) + +# Output generation + +AS_ECHO AH_BOTTOM([#include ]) AC_CONFIG_HEADER([config.h]) diff --git a/kern/kmem.c b/kern/kmem.c index 5423c0a..a811cc5 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -1123,7 +1123,7 @@ kmem_cache_info(struct kmem_cache *cache) mutex_unlock(&cache->lock); } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static struct kmem_cache * kmem_lookup_cache(const char *name) @@ -1184,7 +1184,7 @@ INIT_OP_DEFINE(kmem_setup_shell, INIT_OP_DEP(shell_setup, true), INIT_OP_DEP(thread_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init kmem_bootstrap(void) diff --git a/kern/log.c b/kern/log.c index 10131d1..8356220 100644 --- a/kern/log.c +++ b/kern/log.c @@ -334,7 +334,7 @@ log_run(void *arg) } } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void log_dump(unsigned int level) @@ -412,7 +412,7 @@ INIT_OP_DEFINE(log_setup_shell, INIT_OP_DEP(log_setup, true), INIT_OP_DEP(shell_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init log_setup(void) diff --git a/kern/mutex.h b/kern/mutex.h index 8cb7aac..c0a2c6e 100644 --- a/kern/mutex.h +++ b/kern/mutex.h @@ -23,18 +23,16 @@ #ifndef _KERN_MUTEX_H #define _KERN_MUTEX_H -#if defined(X15_MUTEX_PI) && defined(X15_MUTEX_ADAPTIVE) -#error "only one of X15_MUTEX_PI and X15_MUTEX_ADAPTIVE may be defined" -#endif - #include -#if defined(X15_MUTEX_PI) -#include -#elif defined(X15_MUTEX_ADAPTIVE) +#if defined(X15_USE_MUTEX_ADAPTIVE) #include -#else +#elif defined(X15_USE_MUTEX_PI) +#include +#elif defined(X15_USE_MUTEX_PLAIN) #include +#else +#error "unknown mutex implementation" #endif #include diff --git a/kern/mutex_types.h b/kern/mutex_types.h index eb2bc33..f0f8240 100644 --- a/kern/mutex_types.h +++ b/kern/mutex_types.h @@ -21,12 +21,14 @@ #ifndef _KERN_MUTEX_TYPES_H #define _KERN_MUTEX_TYPES_H -#if defined(X15_MUTEX_PI) -#include -#elif defined(X15_MUTEX_ADAPTIVE) +#if defined(X15_USE_MUTEX_ADAPTIVE) #include -#else +#elif defined(X15_USE_MUTEX_PI) +#include +#elif defined(X15_USE_MUTEX_PLAIN) #include +#else +#error "unknown mutex implementation" #endif #endif /* _KERN_MUTEX_TYPES_H */ diff --git a/kern/shell.h b/kern/shell.h index cf56ceb..f6377f0 100644 --- a/kern/shell.h +++ b/kern/shell.h @@ -25,7 +25,7 @@ #include #include -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL #define SHELL_REGISTER_CMDS(cmds) \ MACRO_BEGIN \ @@ -81,11 +81,11 @@ void shell_start(void); */ int shell_cmd_register(struct shell_cmd *cmd); -#else /* X15_SHELL */ +#else /* X15_ENABLE_SHELL */ #define SHELL_REGISTER_CMDS(cmds) #define shell_setup() #define shell_start() -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ /* * This init operation provides : diff --git a/kern/shutdown.c b/kern/shutdown.c index 1c95041..b85cb1c 100644 --- a/kern/shutdown.c +++ b/kern/shutdown.c @@ -26,7 +26,7 @@ static struct plist shutdown_ops_list; -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void shutdown_shell_halt(int argc, char **argv) @@ -66,7 +66,7 @@ INIT_OP_DEFINE(shutdown_setup_shell, INIT_OP_DEP(shell_setup, true), INIT_OP_DEP(shutdown_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init shutdown_bootstrap(void) diff --git a/kern/syscnt.c b/kern/syscnt.c index 0e3aff7..f1cc95a 100644 --- a/kern/syscnt.c +++ b/kern/syscnt.c @@ -33,7 +33,7 @@ static struct list syscnt_list; static struct mutex syscnt_lock; -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void syscnt_shell_info(int argc, char **argv) @@ -61,7 +61,7 @@ INIT_OP_DEFINE(syscnt_setup_shell, INIT_OP_DEP(shell_setup, true), INIT_OP_DEP(syscnt_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init syscnt_setup(void) diff --git a/kern/task.c b/kern/task.c index ab82ad8..1621056 100644 --- a/kern/task.c +++ b/kern/task.c @@ -64,7 +64,7 @@ task_init(struct task *task, const char *name, struct vm_map *map) strlcpy(task->name, name, sizeof(task->name)); } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void task_shell_info(int argc, char *argv[]) @@ -112,7 +112,7 @@ INIT_OP_DEFINE(task_setup_shell, INIT_OP_DEP(task_setup, true), INIT_OP_DEP(thread_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init task_setup(void) diff --git a/kern/thread.c b/kern/thread.c index 9ee9b99..b6a44bb 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2185,7 +2185,7 @@ thread_setup_runq(struct thread_runq *runq) thread_setup_idler(runq); } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL /* * This function is meant for debugging only. As a result, it uses a weak @@ -2261,7 +2261,7 @@ INIT_OP_DEFINE(thread_setup_shell, INIT_OP_DEP(task_setup, true), INIT_OP_DEP(thread_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static void __init thread_setup_common(unsigned int cpu) diff --git a/vm/vm_map.c b/vm/vm_map.c index 01e0ed5..da0b2cf 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -697,7 +697,7 @@ vm_map_init(struct vm_map *map, struct pmap *pmap, map->pmap = pmap; } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void vm_map_shell_info(int argc, char **argv) @@ -742,7 +742,7 @@ INIT_OP_DEFINE(vm_map_setup_shell, INIT_OP_DEP(task_setup, true), INIT_OP_DEP(vm_map_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init vm_map_bootstrap(void) diff --git a/vm/vm_page.c b/vm/vm_page.c index 19f6f4b..cc6963f 100644 --- a/vm/vm_page.c +++ b/vm/vm_page.c @@ -657,7 +657,7 @@ vm_page_info_common(int (*print_fn)(const char *format, ...)) } } -#ifdef X15_SHELL +#ifdef X15_ENABLE_SHELL static void vm_page_info(void) @@ -691,7 +691,7 @@ INIT_OP_DEFINE(vm_page_setup_shell, INIT_OP_DEP(shell_setup, true), INIT_OP_DEP(vm_page_setup, true)); -#endif /* X15_SHELL */ +#endif /* X15_ENABLE_SHELL */ static int __init vm_page_setup(void) -- cgit v1.2.3 From 5774eb4713e252bd35832c163b0f2b117176c2de Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 3 Sep 2017 14:00:15 +0200 Subject: x86: import macros from the init module --- arch/x86/x15.lds.S | 3 ++- kern/init.h | 8 ++++++-- kern/init_i.h | 6 ------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S index 352ebf4..80b1e65 100644 --- a/arch/x86/x15.lds.S +++ b/arch/x86/x15.lds.S @@ -8,6 +8,7 @@ OUTPUT_ARCH(i386) ENTRY(_start) +#include #include #include #include @@ -45,7 +46,7 @@ SECTIONS *(.init.text) *(.init.data) - . = ALIGN(64); + . = ALIGN(INIT_OP_ALIGN); _init_ops = .; *(.init.ops) _init_ops_end = .; diff --git a/kern/init.h b/kern/init.h index d15d950..8f1cacf 100644 --- a/kern/init.h +++ b/kern/init.h @@ -16,8 +16,6 @@ * * * Init sections and operations. - * - * TODO Make the x86 linker script use macros from this header. */ #ifndef _KERN_INIT_H @@ -36,6 +34,12 @@ */ #define INIT_OPS_SECTION .init.ops +/* + * Alignment is important to make sure initialization operations are + * stored as a C array in the reserved init op section. + */ +#define INIT_OP_ALIGN 64 + #ifndef __ASSEMBLER__ #include diff --git a/kern/init_i.h b/kern/init_i.h index 7a6b463..ed59062 100644 --- a/kern/init_i.h +++ b/kern/init_i.h @@ -18,12 +18,6 @@ #ifndef _KERN_INIT_I_H #define _KERN_INIT_I_H -/* - * Alignment is important to make sure initialization operations are - * stored as a C array in the reserved init op section. - */ -#define INIT_OP_ALIGN 64 - #include #include #include -- cgit v1.2.3 From d568619214a94ea0bb1587c597d914495269b1f2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 3 Sep 2017 14:35:30 +0200 Subject: kern/work: new private header --- Makefrag.am | 1 + kern/work.h | 29 ++++++++++------------------- kern/work_i.h | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 19 deletions(-) create mode 100644 kern/work_i.h diff --git a/Makefrag.am b/Makefrag.am index 2a91809..24d700a 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -120,6 +120,7 @@ x15_SOURCES += \ kern/types.h \ kern/work.c \ kern/work.h \ + kern/work_i.h \ kern/xcall.c \ kern/xcall.h diff --git a/kern/work.h b/kern/work.h index 2d7bd62..68801c4 100644 --- a/kern/work.h +++ b/kern/work.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Richard Braun. + * Copyright (c) 2013-2017 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 @@ -33,35 +33,26 @@ */ #define WORK_HIGHPRIO 0x1 /* Use a high priority worker thread */ -struct work; - -/* - * Type for work functions. - */ -typedef void (*work_fn_t)(struct work *); - /* * Deferred work. * * This structure should be embedded in objects related to the work. It * stores the work function and is passed to it as its only parameter. * The function can then find the containing object with the structof macro. - * - * TODO Make private. */ -struct work { - struct work *next; - work_fn_t fn; -}; +struct work; /* * Queue of deferred works for batch scheduling. */ -struct work_queue { - struct work *first; - struct work *last; - unsigned int nr_works; -}; +struct work_queue; + +/* + * Type for work functions. + */ +typedef void (*work_fn_t)(struct work *); + +#include static inline void work_queue_init(struct work_queue *queue) diff --git a/kern/work_i.h b/kern/work_i.h new file mode 100644 index 0000000..b5bc872 --- /dev/null +++ b/kern/work_i.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013-2017 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 3 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 _KERN_WORK_I_H +#define _KERN_WORK_I_H + +struct work { + struct work *next; + work_fn_t fn; +}; + +struct work_queue { + struct work *first; + struct work *last; + unsigned int nr_works; +}; + +#endif /* _KERN_WORK_I_H */ -- cgit v1.2.3 From c69071d2d7b875e16e65b044d1b38995202c1937 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Sun, 3 Sep 2017 15:50:48 +0200 Subject: Minor style fix Replace the single use of array as function argument with a pointer, as described in the coding rules. --- arch/x86/machine/acpi.c | 2 +- doc/style.9.txt | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/machine/acpi.c b/arch/x86/machine/acpi.c index 4ef52e3..f088f2c 100644 --- a/arch/x86/machine/acpi.c +++ b/arch/x86/machine/acpi.c @@ -186,7 +186,7 @@ static struct acpi_gas acpi_reset_reg; static uint8_t acpi_reset_value; static void __init -acpi_table_sig(const struct acpi_sdth *table, char sig[ACPI_SIG_SIZE]) +acpi_table_sig(const struct acpi_sdth *table, char *sig) { memcpy(sig, table->signature, sizeof(table->signature)); sig[4] = '\0'; diff --git a/doc/style.9.txt b/doc/style.9.txt index c4a5778..be59024 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -557,8 +557,6 @@ operator may be used safely to get the size of the array, when it actually always returns the size of a pointer. Therefore, declaring arguments as arrays is strictly forbidden. -TODO Fix historic array argument declarations. - GIT COMMITS ----------- -- cgit v1.2.3 From 2f55bde818763006057337be3f95a6bc41e62433 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 4 Sep 2017 22:46:49 +0200 Subject: kern/kmem: report physical and virtual memory usage --- kern/kmem.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/kern/kmem.c b/kern/kmem.c index a811cc5..60d3fbc 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -232,7 +232,7 @@ kmem_bufctl_to_buf(union kmem_bufctl *bufctl, struct kmem_cache *cache) } static inline bool -kmem_pagealloc_virtual(size_t size) +kmem_pagealloc_is_virtual(size_t size) { return (size > PAGE_SIZE); } @@ -240,7 +240,7 @@ kmem_pagealloc_virtual(size_t size) static void * kmem_pagealloc(size_t size) { - if (kmem_pagealloc_virtual(size)) { + if (kmem_pagealloc_is_virtual(size)) { return vm_kmem_alloc(size); } else { struct vm_page *page; @@ -259,7 +259,7 @@ kmem_pagealloc(size_t size) static void kmem_pagefree(void *ptr, size_t size) { - if (kmem_pagealloc_virtual(size)) { + if (kmem_pagealloc_is_virtual(size)) { vm_kmem_free(ptr, size); } else { struct vm_page *page; @@ -651,7 +651,7 @@ kmem_cache_register(struct kmem_cache *cache, struct kmem_slab *slab) assert(kmem_cache_registration_required(cache)); assert(slab->nr_refs == 0); - virtual = kmem_pagealloc_virtual(cache->slab_size); + virtual = kmem_pagealloc_is_virtual(cache->slab_size); for (va = kmem_slab_buf(slab), end = va + cache->slab_size; va < end; @@ -684,7 +684,7 @@ kmem_cache_lookup(struct kmem_cache *cache, void *buf) assert(kmem_cache_registration_required(cache)); - virtual = kmem_pagealloc_virtual(cache->slab_size); + virtual = kmem_pagealloc_is_virtual(cache->slab_size); va = (uintptr_t)buf; if (virtual) { @@ -1349,11 +1349,17 @@ kmem_free(void *ptr, size_t size) void kmem_info(void) { - size_t mem_usage, mem_reclaimable, total, total_reclaimable; + size_t total_reclaim, total_reclaim_physical, total_reclaim_virtual; + size_t total, total_physical, total_virtual; + size_t mem_usage, mem_reclaim; struct kmem_cache *cache; total = 0; - total_reclaimable = 0; + total_physical = 0; + total_virtual = 0; + total_reclaim = 0; + total_reclaim_physical = 0; + total_reclaim_virtual = 0; printf("kmem: cache obj slab bufs objs bufs " " total reclaimable\n" @@ -1366,19 +1372,30 @@ kmem_info(void) mutex_lock(&cache->lock); mem_usage = (cache->nr_slabs * cache->slab_size) >> 10; - mem_reclaimable = (cache->nr_free_slabs * cache->slab_size) >> 10; + mem_reclaim = (cache->nr_free_slabs * cache->slab_size) >> 10; total += mem_usage; - total_reclaimable += mem_reclaimable; + total_reclaim += mem_reclaim; + + if (kmem_pagealloc_is_virtual(cache->slab_size)) { + total_virtual += mem_usage; + total_reclaim_virtual += mem_reclaim; + } else { + total_physical += mem_usage; + total_reclaim_physical += mem_reclaim; + } printf("kmem: %-19s %6zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", cache->name, cache->obj_size, cache->slab_size >> 10, cache->bufs_per_slab, cache->nr_objs, cache->nr_bufs, - mem_usage, mem_reclaimable); + mem_usage, mem_reclaim); mutex_unlock(&cache->lock); } mutex_unlock(&kmem_cache_list_lock); - printf("total: %zuk reclaimable: %zuk\n", total, total_reclaimable); + printf("total: %zuk (phys: %zuk virt: %zuk), " + "reclaimable: %zuk phys: %zuk virt: %zuk\n", + total, total_physical, total_virtual, + total_reclaim, total_reclaim_physical, total_reclaim_virtual); } -- cgit v1.2.3 From c7efc68f95d5692d2bfdcb3ce698b28a306c74b5 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Mon, 4 Sep 2017 22:57:42 +0200 Subject: kern/rbtree: update from upstream --- kern/rbtree.c | 47 +++++++++++++++++++++++++++++++++++++++++------ kern/rbtree.h | 20 ++++++++++++++++---- kern/rbtree_i.h | 6 +++--- 3 files changed, 60 insertions(+), 13 deletions(-) diff --git a/kern/rbtree.c b/kern/rbtree.c index adce033..95873b9 100644 --- a/kern/rbtree.c +++ b/kern/rbtree.c @@ -26,7 +26,7 @@ /* * Return the index of a node in the children array of its parent. * - * The parent parameter must not be null, and must be the parent of the + * The parent parameter must not be NULL, and must be the parent of the * given node. */ static inline int @@ -175,7 +175,7 @@ void rbtree_insert_rebalance(struct rbtree *tree, struct rbtree_node *parent, int index, struct rbtree_node *node) { - struct rbtree_node *grand_parent, *uncle, *tmp; + struct rbtree_node *grand_parent, *uncle; int left, right; assert(rbtree_node_check_alignment(parent)); @@ -226,9 +226,7 @@ rbtree_insert_rebalance(struct rbtree *tree, struct rbtree_node *parent, */ if (parent->children[right] == node) { rbtree_rotate(tree, parent, left); - tmp = node; - node = parent; - parent = tmp; + parent = node; } /* @@ -244,6 +242,40 @@ rbtree_insert_rebalance(struct rbtree *tree, struct rbtree_node *parent, assert(rbtree_node_is_black(tree->root)); } +void * +rbtree_replace_slot(struct rbtree *tree, rbtree_slot_t slot, + struct rbtree_node *node) +{ + struct rbtree_node *parent, *prev; + int index; + + parent = rbtree_slot_parent(slot); + + if (!parent) { + prev = tree->root; + tree->root = node; + } else { + index = rbtree_slot_index(slot); + assert(rbtree_check_index(index)); + prev = parent->children[index]; + parent->children[index] = node; + } + + assert(prev); + *node = *prev; + + for (size_t i = 0; i < ARRAY_SIZE(node->children); i++) { + if (!node->children[i]) { + continue; + } + + + rbtree_node_set_parent(node->children[i], node); + } + + return prev; +} + void rbtree_remove(struct rbtree *tree, struct rbtree_node *node) { @@ -321,7 +353,7 @@ rbtree_remove(struct rbtree *tree, struct rbtree_node *node) /* * The node has been removed, update the colors. The child pointer can - * be null, in which case it is considered a black leaf. + * be NULL, in which case it is considered a black leaf. */ update_color: if (color == RBTREE_COLOR_RED) { @@ -354,6 +386,8 @@ update_color: brother = parent->children[right]; } + assert(brother != NULL); + /* * Brother has no red child. Recolor and repeat at parent. */ @@ -383,6 +417,7 @@ update_color: * (we already know brother is black), set brother's right child black, * rotate left at parent and leave. */ + assert(brother->children[right] != NULL); rbtree_node_set_color(brother, rbtree_node_color(parent)); rbtree_node_set_black(parent); rbtree_node_set_black(brother->children[right]); diff --git a/kern/rbtree.h b/kern/rbtree.h index 3de240b..e805bd9 100644 --- a/kern/rbtree.h +++ b/kern/rbtree.h @@ -48,6 +48,11 @@ struct rbtree; */ typedef uintptr_t rbtree_slot_t; +/* + * Static tree initializer. + */ +#define RBTREE_INITIALIZER { NULL } + #include /* @@ -209,7 +214,7 @@ MACRO_END * * This macro essentially acts as rbtree_lookup() but in addition to a node, * it also returns a slot, which identifies an insertion point in the tree. - * If the returned node is null, the slot can be used by rbtree_insert_slot() + * If the returned node is NULL, the slot can be used by rbtree_insert_slot() * to insert without the overhead of an additional lookup. * * The constraints that apply to the key parameter are the same as for @@ -247,7 +252,7 @@ MACRO_END * obtain the insertion point with a standard lookup. The insertion point * is obtained by calling rbtree_lookup_slot(). In addition, the new node * must not compare equal to an existing node in the tree (i.e. the slot - * must denote a null node). + * must denote a NULL node). */ static inline void rbtree_insert_slot(struct rbtree *tree, rbtree_slot_t slot, @@ -261,12 +266,19 @@ rbtree_insert_slot(struct rbtree *tree, rbtree_slot_t slot, rbtree_insert_rebalance(tree, parent, index, node); } +/* + * Replace a node at an insertion point in a tree. + * + * The given node must compare strictly equal to the previous node, + * which is returned on completion. + */ +void * rbtree_replace_slot(struct rbtree *tree, rbtree_slot_t slot, + struct rbtree_node *node); + /* * Remove a node from a tree. * * After completion, the node is stale. - * - * TODO rbtree_replace. */ void rbtree_remove(struct rbtree *tree, struct rbtree_node *node); diff --git a/kern/rbtree_i.h b/kern/rbtree_i.h index 944148e..973899b 100644 --- a/kern/rbtree_i.h +++ b/kern/rbtree_i.h @@ -54,7 +54,7 @@ struct rbtree { * Masks applied on the parent member of a node to obtain either the * color or the parent address. */ -#define RBTREE_COLOR_MASK ((uintptr_t)1) +#define RBTREE_COLOR_MASK ((uintptr_t)0x1) #define RBTREE_PARENT_MASK (~(uintptr_t)0x3) /* @@ -67,7 +67,7 @@ struct rbtree { * Masks applied on slots to obtain either the child index or the parent * address. */ -#define RBTREE_SLOT_INDEX_MASK ((uintptr_t)0x1) +#define RBTREE_SLOT_INDEX_MASK 0x1UL #define RBTREE_SLOT_PARENT_MASK (~RBTREE_SLOT_INDEX_MASK) /* @@ -142,7 +142,7 @@ rbtree_slot_index(rbtree_slot_t slot) * Insert a node in a tree, rebalancing it if necessary. * * The index parameter is the index in the children array of the parent where - * the new node is to be inserted. It is ignored if the parent is null. + * the new node is to be inserted. It is ignored if the parent is NULL. * * This function is intended to be used by the rbtree_insert() macro only. */ -- cgit v1.2.3 From 51cb42fd1f221736b470a85b423c41eac5865373 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 5 Sep 2017 20:34:16 +0200 Subject: kern/thread: new thread_get_tcb function --- kern/thread.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kern/thread.h b/kern/thread.h index 0797343..2d28aa8 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -392,6 +392,12 @@ thread_real_global_priority(const struct thread *thread) */ const char * thread_sched_class_to_str(unsigned char sched_class); +static inline struct tcb * +thread_get_tcb(struct thread *thread) +{ + return &thread->tcb; +} + static inline struct thread * thread_from_tcb(struct tcb *tcb) { -- cgit v1.2.3 From 429fa916855575e10244245a100120ad2777cc8a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 5 Sep 2017 20:34:47 +0200 Subject: x86/{pmap,tcb}: don't use tsd for the update oplist --- arch/x86/machine/pmap.c | 35 ++++++++++++++++++++++------------- arch/x86/machine/pmap.h | 5 +++-- arch/x86/machine/tcb.c | 10 ++++++++-- arch/x86/machine/tcb.h | 27 +++++++++++++++++++++++++-- kern/thread.c | 3 ++- kern/thread_i.h | 5 ++--- 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index 7ab70bf..f8c6d03 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -191,8 +192,6 @@ struct pmap_update_oplist { struct pmap_update_op ops[PMAP_UPDATE_MAX_OPS]; }; -static unsigned int pmap_oplist_tsd_key __read_mostly; - /* * Statically allocated data for the main booter thread. */ @@ -676,11 +675,8 @@ pmap_update_oplist_create(struct pmap_update_oplist **oplistp) } static void -pmap_update_oplist_destroy(void *arg) +pmap_update_oplist_destroy(struct pmap_update_oplist *oplist) { - struct pmap_update_oplist *oplist; - - oplist = arg; kmem_cache_free(&pmap_update_oplist_cache, oplist); } @@ -689,7 +685,7 @@ pmap_update_oplist_get(void) { struct pmap_update_oplist *oplist; - oplist = thread_get_specific(pmap_oplist_tsd_key); + oplist = tcb_get_pmap_update_oplist(tcb_current()); assert(oplist != NULL); return oplist; } @@ -855,8 +851,7 @@ pmap_bootstrap(void) pmap_syncer_init(cpu_local_ptr(pmap_syncer), 0); pmap_update_oplist_ctor(&pmap_booter_oplist); - thread_key_create(&pmap_oplist_tsd_key, pmap_update_oplist_destroy); - thread_set_specific(pmap_oplist_tsd_key, &pmap_booter_oplist); + tcb_set_pmap_update_oplist(tcb_current(), &pmap_booter_oplist); cpumap_zero(&pmap_booter_cpumap); cpumap_set(&pmap_booter_cpumap, 0); @@ -1035,6 +1030,7 @@ pmap_mp_setup(void) struct thread_attr attr; struct pmap_syncer *syncer; struct cpumap *cpumap; + struct tcb *tcb; unsigned int cpu; int error; @@ -1064,8 +1060,9 @@ pmap_mp_setup(void) panic("pmap: unable to create syncer thread"); } - oplist = thread_tsd_get(syncer->thread, pmap_oplist_tsd_key); - thread_tsd_set(syncer->thread, pmap_oplist_tsd_key, NULL); + tcb = thread_get_tcb(syncer->thread); + oplist = tcb_get_pmap_update_oplist(tcb); + tcb_set_pmap_update_oplist(tcb, NULL); kmem_cache_free(&pmap_update_oplist_cache, oplist); } @@ -1079,7 +1076,7 @@ pmap_mp_setup(void) } int -pmap_thread_init(struct thread *thread) +pmap_thread_build(struct thread *thread) { struct pmap_update_oplist *oplist; int error; @@ -1090,10 +1087,22 @@ pmap_thread_init(struct thread *thread) return error; } - thread_tsd_set(thread, pmap_oplist_tsd_key, oplist); + tcb_set_pmap_update_oplist(thread_get_tcb(thread), oplist); return 0; } +void +pmap_thread_cleanup(struct thread *thread) +{ + struct pmap_update_oplist *oplist; + + oplist = tcb_get_pmap_update_oplist(thread_get_tcb(thread)); + + if (oplist) { + pmap_update_oplist_destroy(oplist); + } +} + int pmap_kextract(uintptr_t va, phys_addr_t *pap) { diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 114dcda..8ef28c7 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -221,9 +221,10 @@ void pmap_ap_setup(void); void pmap_mp_setup(void); /* - * Initialize pmap thread-specific data for the given thread. + * Build/clean up pmap thread-local data for the given thread. */ -int pmap_thread_init(struct thread *thread); +int pmap_thread_build(struct thread *thread); +void pmap_thread_cleanup(struct thread *thread); /* * Extract a mapping from the kernel map. diff --git a/arch/x86/machine/tcb.c b/arch/x86/machine/tcb.c index b54945c..6978811 100644 --- a/arch/x86/machine/tcb.c +++ b/arch/x86/machine/tcb.c @@ -74,11 +74,11 @@ tcb_stack_forge(struct tcb *tcb, void (*fn)(void *), void *arg) #endif /* __LP64__ */ int -tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) +tcb_build(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) { int error; - error = pmap_thread_init(thread_from_tcb(tcb)); + error = pmap_thread_build(thread_from_tcb(tcb)); if (error) { return error; @@ -90,6 +90,12 @@ tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg) return 0; } +void +tcb_cleanup(struct tcb *tcb) +{ + pmap_thread_cleanup(thread_from_tcb(tcb)); +} + void __init tcb_load(struct tcb *tcb) { diff --git a/arch/x86/machine/tcb.h b/arch/x86/machine/tcb.h index 9107f7c..004cf92 100644 --- a/arch/x86/machine/tcb.h +++ b/arch/x86/machine/tcb.h @@ -34,23 +34,34 @@ */ #define TCB_STACK_SIZE PAGE_SIZE +/* + * Forward declaration. + */ +struct pmap_update_oplist; + /* * Thread control block. */ struct tcb { uintptr_t bp; uintptr_t sp; + struct pmap_update_oplist *oplist; }; /* - * Initialize a TCB. + * Build a TCB. * * Prepare the given stack for execution. The context is defined so that it * will call thread_main(fn, arg) with interrupts disabled when loaded. * * In addition, initialize any thread-local machine-specific data. */ -int tcb_init(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg); +int tcb_build(struct tcb *tcb, void *stack, void (*fn)(void *), void *arg); + +/* + * Release all resources held by a TCB. + */ +void tcb_cleanup(struct tcb *tcb); static inline struct tcb * tcb_current(void) @@ -66,6 +77,18 @@ tcb_set_current(struct tcb *tcb) cpu_local_assign(tcb_current_ptr, tcb); } +static inline void +tcb_set_pmap_update_oplist(struct tcb *tcb, struct pmap_update_oplist *oplist) +{ + tcb->oplist = oplist; +} + +static inline struct pmap_update_oplist * +tcb_get_pmap_update_oplist(struct tcb *tcb) +{ + return tcb->oplist; +} + /* * Load a TCB. * diff --git a/kern/thread.c b/kern/thread.c index b6a44bb..c04635b 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -1833,7 +1833,7 @@ thread_init(struct thread *thread, void *stack, thread->flags |= THREAD_DETACHED; } - error = tcb_init(&thread->tcb, stack, fn, arg); + error = tcb_build(&thread->tcb, stack, fn, arg); if (error) { goto error_tcb; @@ -1964,6 +1964,7 @@ thread_destroy(struct thread *thread) turnstile_destroy(thread->priv_turnstile); sleepq_destroy(thread->priv_sleepq); thread_free_stack(thread->stack); + tcb_cleanup(&thread->tcb); kmem_cache_free(&thread_cache, thread); } diff --git a/kern/thread_i.h b/kern/thread_i.h index 4218b50..c8a85f5 100644 --- a/kern/thread_i.h +++ b/kern/thread_i.h @@ -168,10 +168,9 @@ struct thread { }; /* - * Thread-specific data should only be used by architecture-dependent code. - * For machine-independent code, new member variables should be added. + * Thread-specific data. * - * TODO move those to the TCB and remove. + * TODO Make optional. */ void *tsd[THREAD_KEYS_MAX]; -- cgit v1.2.3 From 1d6ea814d13a05fd4d64e0764acf667f67679092 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 5 Sep 2017 21:11:56 +0200 Subject: doc/style(9): add build and cleanup to the common function names --- doc/style.9.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/style.9.txt b/doc/style.9.txt index be59024..1b5108f 100644 --- a/doc/style.9.txt +++ b/doc/style.9.txt @@ -511,12 +511,15 @@ for the most common operations. *setup*:: This function is used for module initialization. *init*:: - Object initialization, without memory allocation. Object initialization - should normally never fail. + Object initialization that may not fail (e.g. without memory allocation). +*build*:: + Object construction, i.e. initialization that may fail. +*cleanup*:: + Object clean-up, releasing internal resources but not the object itself. *create*:: - Object creation, including both allocation and initialization. + Object creation, including both allocation and construction. *destroy*:: - Object destruction. + Object destruction, releasing all resources including the object itself. *ref*:: Add a reference to an object. *unref*:: -- cgit v1.2.3 From 8b1b21a33dbe4b114400090ff385c4bf8daea0c8 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Tue, 5 Sep 2017 22:04:21 +0200 Subject: kern/xcall: make sure functions are always run from interrupt context --- kern/xcall.c | 12 +----------- kern/xcall.h | 3 ++- test/test_xcall.c | 2 ++ 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/kern/xcall.c b/kern/xcall.c index b5ed4b2..9cef07a 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -127,22 +127,13 @@ xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu) struct xcall_cpu_data *local_data, *remote_data; struct xcall *call; + assert(cpu_intr_enabled()); assert(fn != NULL); remote_data = percpu_ptr(xcall_cpu_data, cpu); thread_preempt_disable(); - /* TODO Fix to match interrupt context semantics */ - if (cpu == cpu_id()) { - unsigned long flags; - - cpu_intr_save(&flags); - fn(arg); - cpu_intr_restore(flags); - goto out; - } - local_data = xcall_cpu_data_get(); call = xcall_cpu_data_get_send_call(local_data, cpu); xcall_set(call, fn, arg); @@ -159,7 +150,6 @@ xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu) spinlock_unlock(&remote_data->lock); -out: thread_preempt_enable(); } diff --git a/kern/xcall.h b/kern/xcall.h index 27b6af2..df36487 100644 --- a/kern/xcall.h +++ b/kern/xcall.h @@ -36,7 +36,8 @@ typedef void (*xcall_fn_t)(void *arg); * has finished running on the target processor, with the side effects of * the function visible. * - * The function is run in interrupt context. + * The function is run in interrupt context. Interrupts must be enabled + * when calling this function. */ void xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu); diff --git a/test/test_xcall.c b/test/test_xcall.c index 4e02d6f..f516401 100644 --- a/test/test_xcall.c +++ b/test/test_xcall.c @@ -37,6 +37,8 @@ test_fn(void *arg) { (void)arg; + assert(thread_interrupted()); + printf("function called, running on cpu%u\n", cpu_id()); test_done = 1; } -- cgit v1.2.3 From c4ef7cdfd1dd33674b1e3b31fa35df389a440df9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 00:19:50 +0200 Subject: configure.ac: slightly simplify test module handling This is still not as good as it should be, but autotools conditionals are a pain. --- configure.ac | 56 ++++++++++++++++++++------------------------------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/configure.ac b/configure.ac index 6077696..c544a5d 100644 --- a/configure.ac +++ b/configure.ac @@ -48,7 +48,7 @@ AC_ARG_ENABLE([test-module], [AS_HELP_STRING([--enable-test-module=TEST_MODULE], [run the given test module instead of booting])], [enable_test_module=$enableval], - [enable_test_module=no]) + [enable_test_module=]) AC_ARG_ENABLE([thread-stack-guard], [AS_HELP_STRING([--enable-thread-stack-guard], @@ -101,41 +101,25 @@ AC_MSG_NOTICE([diagnostics shell: $enable_shell]) # Test module selection -m4_define([ENABLE_TEST_MODULE], - [AS_CASE(["$enable_test_module"], - [llsync_defer], [test_llsync_defer=yes], - [mutex], [test_mutex=yes], - [mutex_pi], [test_mutex_pi=yes], - [pmap_update_mp], [test_pmap_update_mp=yes], - [sref_dirty_zeroes], [test_sref_dirty_zeroes=yes], - [sref_noref], [test_sref_noref=yes], - [sref_weakref], [test_sref_weakref=yes], - [vm_page_fill], [test_vm_page_fill=yes], - [xcall], [test_xcall=yes], - [AC_MSG_ERROR([invalid test module])]) - AC_DEFINE_UNQUOTED([X15_RUN_TEST_MODULE], [], - [run test module instead of booting]) - AC_MSG_NOTICE([test module enabled: $enable_test_module])]) - -AS_IF([test x"$enable_test_module" != xno], [ENABLE_TEST_MODULE]) -AM_CONDITIONAL([ENABLE_TEST_LLSYNC_DEFER], - [test x"$test_llsync_defer" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_MUTEX], - [test x"$test_mutex" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_MUTEX_PI], - [test x"$test_mutex_pi" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_PMAP_UPDATE_MP], - [test x"$test_pmap_update_mp" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_SREF_DIRTY_ZEROES], - [test x"$test_sref_dirty_zeroes" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_SREF_NOREF], - [test x"$test_sref_noref" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_SREF_WEAKREF], - [test x"$test_sref_weakref" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_VM_PAGE_FILL], - [test x"$test_vm_page_fill" = xyes]) -AM_CONDITIONAL([ENABLE_TEST_XCALL], - [test x"$test_xcall" = xyes]) +m4_define([DEFINE_TEST_MODULE], + [AM_CONDITIONAL([$1], [test x"$enable_test_module" = x"$2"]) + AM_COND_IF([$1], [AC_DEFINE_UNQUOTED([X15_RUN_TEST_MODULE], [], + [run test module instead of booting])])]) + +DEFINE_TEST_MODULE(ENABLE_TEST_LLSYNC_DEFER, llsync_defer) +DEFINE_TEST_MODULE(ENABLE_TEST_MUTEX, mutex) +DEFINE_TEST_MODULE(ENABLE_TEST_MUTEX_PI, mutex_pi) +DEFINE_TEST_MODULE(ENABLE_TEST_PMAP_UPDATE_MP, pmap_update_mp) +DEFINE_TEST_MODULE(ENABLE_TEST_SREF_DIRTY_ZEROES, sref_dirty_zeroes) +DEFINE_TEST_MODULE(ENABLE_TEST_SREF_NOREF, sref_noref) +DEFINE_TEST_MODULE(ENABLE_TEST_SREF_WEAKREF, sref_weakref) +DEFINE_TEST_MODULE(ENABLE_TEST_VM_PAGE_FILL, vm_page_fill) +DEFINE_TEST_MODULE(ENABLE_TEST_XCALL, xcall) + +AS_IF([test "$enable_test_module" -a -f test/test_$enable_test_module.c], + [AC_MSG_NOTICE([test module enabled: $enable_test_module])], + [test "$enable_test_module"], + [AC_MSG_ERROR([invalid test module])]) # Thread stack guard selection -- cgit v1.2.3 From 922d9451fa7104ceb97393f84918c3a186cefe4c Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 20:42:42 +0200 Subject: kern/rtmutex: fix priority propagation on unlock --- kern/rtmutex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kern/rtmutex.c b/kern/rtmutex.c index 0963751..b6c4645 100644 --- a/kern/rtmutex.c +++ b/kern/rtmutex.c @@ -211,7 +211,7 @@ rtmutex_unlock_slow(struct rtmutex *rtmutex) owner = rtmutex_unlock_fast(rtmutex); if (!(owner & RTMUTEX_CONTENDED)) { - return; + goto out; } } @@ -224,7 +224,7 @@ rtmutex_unlock_slow(struct rtmutex *rtmutex) turnstile_release(turnstile); - /* TODO Make private, use thread_set_priority_propagation_needed instead */ +out: thread_propagate_priority(); } -- cgit v1.2.3 From 76161a62dd157ec9c7692098356a439b408ced60 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 20:42:42 +0200 Subject: kern/timer: add TODO entry about hash parameters --- kern/timer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kern/timer.c b/kern/timer.c index 57f1765..c73a34a 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -18,6 +18,8 @@ * This implementation is based on "Hashed and Hierarchical Timing Wheels: * Efficient Data Structures for Implementing a Timer Facility" by George * Varghese and Tony Lauck. Specifically, it implements scheme 6.1.2. + * + * TODO Analyse hash parameters. */ #include -- cgit v1.2.3 From 4a9e56baddebd6b0f2c145d6cdb4425680c8a185 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 20:42:42 +0200 Subject: Use new thread_check_intr_context function consistently --- kern/clock.c | 3 +-- kern/console.c | 2 +- kern/intr.c | 3 +-- kern/llsync.c | 3 +-- kern/sref.c | 3 +-- kern/thread.c | 4 ++-- kern/thread.h | 17 ++++++++--------- kern/timer.c | 3 +-- kern/work.c | 3 +-- kern/xcall.c | 2 +- 10 files changed, 18 insertions(+), 25 deletions(-) diff --git a/kern/clock.c b/kern/clock.c index b4a6f04..19f823c 100644 --- a/kern/clock.c +++ b/kern/clock.c @@ -68,8 +68,7 @@ void clock_tick_intr(void) { struct clock_cpu_data *cpu_data; - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); + assert(thread_check_intr_context()); if (cpu_id() == 0) { #ifdef ATOMIC_HAVE_64B_OPS diff --git a/kern/console.c b/kern/console.c index ebbdb80..0b5c4f8 100644 --- a/kern/console.c +++ b/kern/console.c @@ -173,7 +173,7 @@ console_register(struct console *console) void console_intr(struct console *console, const char *s) { - assert(!cpu_intr_enabled()); + assert(thread_check_intr_context()); if (*s == '\0') { return; diff --git a/kern/intr.c b/kern/intr.c index 8579b74..9a1ad71 100644 --- a/kern/intr.c +++ b/kern/intr.c @@ -423,8 +423,7 @@ intr_handle(unsigned int intr) struct intr_entry *entry; int error; - assert(!cpu_intr_enabled()); - assert(thread_interrupted()); + assert(thread_check_intr_context()); entry = intr_get_entry(intr); diff --git a/kern/llsync.c b/kern/llsync.c index 34a3fa0..36233fa 100644 --- a/kern/llsync.c +++ b/kern/llsync.c @@ -255,8 +255,7 @@ llsync_report_periodic_event(void) struct llsync_cpu_data *cpu_data; unsigned int gcid; - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); + assert(thread_check_intr_context()); cpu_data = llsync_get_cpu_data(); diff --git a/kern/sref.c b/kern/sref.c index dd81ca6..7775a5c 100644 --- a/kern/sref.c +++ b/kern/sref.c @@ -984,8 +984,7 @@ sref_report_periodic_event(void) { struct sref_cache *cache; - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); + assert(thread_check_intr_context()); cache = sref_cache_get(); diff --git a/kern/thread.c b/kern/thread.c index c04635b..b5a3ae4 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2630,7 +2630,7 @@ thread_schedule_intr(void) { struct thread_runq *runq; - thread_assert_interrupted(); + assert(thread_check_intr_context()); runq = thread_runq_local(); syscnt_inc(&runq->sc_schedule_intrs); @@ -2643,7 +2643,7 @@ thread_report_periodic_event(void) struct thread_runq *runq; struct thread *thread; - thread_assert_interrupted(); + assert(thread_check_intr_context()); runq = thread_runq_local(); thread = thread_self(); diff --git a/kern/thread.h b/kern/thread.h index 2d28aa8..164eed7 100644 --- a/kern/thread.h +++ b/kern/thread.h @@ -642,6 +642,14 @@ thread_interrupted(void) return (thread_self()->intr != 0); } +static inline bool +thread_check_intr_context(void) +{ + return thread_interrupted() + && !cpu_intr_enabled() + && !thread_preempt_enabled(); +} + static inline void thread_intr_enter(void) { @@ -673,15 +681,6 @@ thread_intr_leave(void) } } -/* TODO Use in interrupt handlers instead of manual interrupt/preemption checks */ -static inline void -thread_assert_interrupted(void) -{ - assert(thread_interrupted()); - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); -} - /* * Lockless synchronization read-side critical section nesting counter * control functions. diff --git a/kern/timer.c b/kern/timer.c index c73a34a..5a94860 100644 --- a/kern/timer.c +++ b/kern/timer.c @@ -527,8 +527,7 @@ timer_report_periodic_event(void) struct hlist timers; uint64_t ticks, now; - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); + assert(thread_check_intr_context()); now = clock_get_time(); hlist_init(&timers); diff --git a/kern/work.c b/kern/work.c index c0eb0bb..ea3eb1f 100644 --- a/kern/work.c +++ b/kern/work.c @@ -537,8 +537,7 @@ work_report_periodic_event(void) { struct work_queue queue, highprio_queue; - assert(!cpu_intr_enabled()); - assert(!thread_preempt_enabled()); + assert(thread_check_intr_context()); work_pool_shift_queues(cpu_local_ptr(work_pool_cpu_main), &queue); work_pool_shift_queues(cpu_local_ptr(work_pool_cpu_highprio), diff --git a/kern/xcall.c b/kern/xcall.c index 9cef07a..1251f28 100644 --- a/kern/xcall.c +++ b/kern/xcall.c @@ -159,7 +159,7 @@ xcall_intr(void) struct xcall_cpu_data *cpu_data; struct xcall *call; - thread_assert_interrupted(); + assert(thread_check_intr_context()); cpu_data = xcall_cpu_data_get(); call = xcall_cpu_data_get_recv_call(cpu_data); -- cgit v1.2.3 From ce388aba04cc54be7d44ec22569559381939bd4a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 20:42:42 +0200 Subject: kern/kmem: fix info summary format --- kern/kmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/kmem.c b/kern/kmem.c index 60d3fbc..ab33a86 100644 --- a/kern/kmem.c +++ b/kern/kmem.c @@ -1395,7 +1395,7 @@ kmem_info(void) mutex_unlock(&kmem_cache_list_lock); printf("total: %zuk (phys: %zuk virt: %zuk), " - "reclaimable: %zuk phys: %zuk virt: %zuk\n", + "reclaim: %zuk (phys: %zuk virt: %zuk)\n", total, total_physical, total_virtual, total_reclaim, total_reclaim_physical, total_reclaim_virtual); } -- cgit v1.2.3 From 7f4858978a49855b23904169db5e44d2fbdb428e Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 21:12:00 +0200 Subject: vm: rename min/max macros to start/end variants The real problem actually only applies to "max" names, for which the value is ambiguous, as "max" usually implies the value is included in the associated range, which is not the case for these macros. --- arch/x86/machine/pmap.c | 32 ++++++++++++++++---------------- arch/x86/machine/pmap.h | 33 ++++++++++++++++----------------- vm/vm_kmem.c | 6 +++--- vm/vm_kmem.h | 4 ++-- vm/vm_map.c | 4 ++-- vm/vm_page.h | 8 ++++---- 6 files changed, 43 insertions(+), 44 deletions(-) diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index f8c6d03..fb251b0 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -447,12 +447,12 @@ pmap_setup_paging(void) directmap_end = biosmem_directmap_end(); - if (directmap_end > (PMAP_MAX_DIRECTMAP_ADDRESS - - PMAP_MIN_DIRECTMAP_ADDRESS)) { + if (directmap_end > (PMAP_END_DIRECTMAP_ADDRESS + - PMAP_START_DIRECTMAP_ADDRESS)) { boot_panic(pmap_panic_directmap_msg); } - va = PMAP_MIN_DIRECTMAP_ADDRESS; + va = PMAP_START_DIRECTMAP_ADDRESS; pa = 0; for (i = 0; i < directmap_end; i += pgsize) { @@ -510,14 +510,14 @@ pmap_ap_setup_paging(void) #define pmap_assert_range(pmap, start, end) \ MACRO_BEGIN \ assert((start) < (end)); \ - assert(((end) <= PMAP_MIN_DIRECTMAP_ADDRESS) \ - || ((start) >= PMAP_MAX_DIRECTMAP_ADDRESS)); \ + assert(((end) <= PMAP_START_DIRECTMAP_ADDRESS) \ + || ((start) >= PMAP_END_DIRECTMAP_ADDRESS)); \ \ if ((pmap) == kernel_pmap) { \ - assert(((start) >= PMAP_MIN_KMEM_ADDRESS) \ - && ((end) <= PMAP_MAX_KMEM_ADDRESS)); \ + assert(((start) >= PMAP_START_KMEM_ADDRESS) \ + && ((end) <= PMAP_END_KMEM_ADDRESS)); \ } else { \ - assert((end) <= PMAP_MAX_ADDRESS); \ + assert((end) <= PMAP_END_ADDRESS); \ } \ MACRO_END @@ -584,7 +584,7 @@ pmap_walk_vas(uintptr_t start, uintptr_t end, pmap_walk_fn_t walk_fn) assert(vm_page_aligned(start)); assert(start < end); #ifdef __LP64__ - assert((start < PMAP_MAX_ADDRESS) || (start >= PMAP_MIN_KERNEL_ADDRESS)); + assert((start < PMAP_END_ADDRESS) || (start >= PMAP_START_KERNEL_ADDRESS)); #endif /* __LP64__ */ va = start; @@ -593,8 +593,8 @@ pmap_walk_vas(uintptr_t start, uintptr_t end, pmap_walk_fn_t walk_fn) do { #ifdef __LP64__ /* Handle long mode canonical form */ - if (va == PMAP_MAX_ADDRESS) { - va = PMAP_MIN_KERNEL_ADDRESS; + if (va == PMAP_END_ADDRESS) { + va = PMAP_START_KERNEL_ADDRESS; } #endif /* __LP64__ */ @@ -642,7 +642,7 @@ pmap_setup_global_page(phys_addr_t ptp_pa, unsigned int index, static void __init pmap_setup_global_pages(void) { - pmap_walk_vas(PMAP_MIN_KERNEL_ADDRESS, PMAP_MAX_KERNEL_ADDRESS, + pmap_walk_vas(PMAP_START_KERNEL_ADDRESS, PMAP_END_KERNEL_ADDRESS, pmap_setup_global_page); pmap_pt_levels[0].mask |= PMAP_PTE_G; cpu_enable_global_pages(); @@ -894,7 +894,7 @@ pmap_setup_set_ptp_type(phys_addr_t ptp_pa, unsigned int index, static void __init pmap_setup_fix_ptps(void) { - pmap_walk_vas(PMAP_MIN_ADDRESS, PMAP_MAX_KERNEL_ADDRESS, + pmap_walk_vas(PMAP_START_ADDRESS, PMAP_END_KERNEL_ADDRESS, pmap_setup_set_ptp_type); } @@ -959,8 +959,8 @@ pmap_copy_cpu_table_recursive(const pmap_pte_t *sptp, unsigned int level, i++, va = P2END(va, 1UL << pt_level->skip)) { #ifdef __LP64__ /* Handle long mode canonical form */ - if (va == PMAP_MAX_ADDRESS) { - va = PMAP_MIN_KERNEL_ADDRESS; + if (va == PMAP_END_ADDRESS) { + va = PMAP_START_KERNEL_ADDRESS; } #endif /* __LP64__ */ @@ -1019,7 +1019,7 @@ pmap_copy_cpu_table(unsigned int cpu) dptp = vm_page_direct_ptr(page); #endif /* X15_X86_PAE */ - pmap_copy_cpu_table_recursive(sptp, level, dptp, PMAP_MIN_ADDRESS); + pmap_copy_cpu_table_recursive(sptp, level, dptp, PMAP_START_ADDRESS); } void __init diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 8ef28c7..96fc21e 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -16,7 +16,6 @@ * * * TODO Comment. - * TODO Rename MIN/MAX to START/END. */ #ifndef _X86_PMAP_H @@ -31,34 +30,34 @@ /* * User space boundaries. */ -#define PMAP_MIN_ADDRESS DECL_CONST(0, UL) +#define PMAP_START_ADDRESS DECL_CONST(0, UL) #ifdef __LP64__ -#define PMAP_MAX_ADDRESS DECL_CONST(0x800000000000, UL) +#define PMAP_END_ADDRESS DECL_CONST(0x800000000000, UL) #else /* __LP64__ */ -#define PMAP_MAX_ADDRESS DECL_CONST(0xc0000000, UL) +#define PMAP_END_ADDRESS DECL_CONST(0xc0000000, UL) #endif/* __LP64__ */ /* * Kernel space boundaries. */ #ifdef __LP64__ -#define PMAP_MIN_KERNEL_ADDRESS DECL_CONST(0xffff800000000000, UL) -#define PMAP_MAX_KERNEL_ADDRESS DECL_CONST(0xfffffffffffff000, UL) +#define PMAP_START_KERNEL_ADDRESS DECL_CONST(0xffff800000000000, UL) +#define PMAP_END_KERNEL_ADDRESS DECL_CONST(0xfffffffffffff000, UL) #else /* __LP64__ */ -#define PMAP_MIN_KERNEL_ADDRESS PMAP_MAX_ADDRESS -#define PMAP_MAX_KERNEL_ADDRESS DECL_CONST(0xfffff000, UL) +#define PMAP_START_KERNEL_ADDRESS PMAP_END_ADDRESS +#define PMAP_END_KERNEL_ADDRESS DECL_CONST(0xfffff000, UL) #endif /* __LP64__ */ /* * Direct physical mapping boundaries. */ #ifdef __LP64__ -#define PMAP_MIN_DIRECTMAP_ADDRESS PMAP_MIN_KERNEL_ADDRESS -#define PMAP_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xffffc00000000000, UL) +#define PMAP_START_DIRECTMAP_ADDRESS PMAP_START_KERNEL_ADDRESS +#define PMAP_END_DIRECTMAP_ADDRESS DECL_CONST(0xffffc00000000000, UL) #else /* __LP64__ */ -#define PMAP_MIN_DIRECTMAP_ADDRESS PMAP_MAX_ADDRESS -#define PMAP_MAX_DIRECTMAP_ADDRESS DECL_CONST(0xf8000000, UL) +#define PMAP_START_DIRECTMAP_ADDRESS PMAP_END_ADDRESS +#define PMAP_END_DIRECTMAP_ADDRESS DECL_CONST(0xf8000000, UL) #endif /* __LP64__ */ /* @@ -70,9 +69,9 @@ * the -mcmodel=kernel gcc option). */ #ifdef __LP64__ -#define PMAP_KERNEL_OFFSET DECL_CONST(0xffffffff80000000, UL) +#define PMAP_KERNEL_OFFSET DECL_CONST(0xffffffff80000000, UL) #else /* __LP64__ */ -#define PMAP_KERNEL_OFFSET PMAP_MIN_DIRECTMAP_ADDRESS +#define PMAP_KERNEL_OFFSET PMAP_START_DIRECTMAP_ADDRESS #endif /* __LP64__ */ /* @@ -81,12 +80,12 @@ * In addition to the direct physical mapping, the kernel has its own virtual * memory space. */ -#define PMAP_MIN_KMEM_ADDRESS PMAP_MAX_DIRECTMAP_ADDRESS +#define PMAP_START_KMEM_ADDRESS PMAP_END_DIRECTMAP_ADDRESS #ifdef __LP64__ -#define PMAP_MAX_KMEM_ADDRESS PMAP_KERNEL_OFFSET +#define PMAP_END_KMEM_ADDRESS PMAP_KERNEL_OFFSET #else /* __LP64__ */ -#define PMAP_MAX_KMEM_ADDRESS PMAP_MAX_KERNEL_ADDRESS +#define PMAP_END_KMEM_ADDRESS PMAP_END_KERNEL_ADDRESS #endif /* __LP64__ */ /* diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index d526804..0583bb9 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -44,8 +44,8 @@ static struct vm_object vm_kmem_kernel_object; static uint64_t vm_kmem_offset(uintptr_t va) { - assert(va >= PMAP_MIN_KMEM_ADDRESS); - return va - PMAP_MIN_KMEM_ADDRESS; + assert(va >= PMAP_START_KMEM_ADDRESS); + return va - PMAP_START_KMEM_ADDRESS; } static int __init @@ -53,7 +53,7 @@ vm_kmem_setup(void) { uint64_t size; - size = vm_kmem_offset(PMAP_MAX_KMEM_ADDRESS); + size = vm_kmem_offset(PMAP_END_KMEM_ADDRESS); vm_object_init(&vm_kmem_kernel_object, size); return 0; } diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index e10c220..9bb90dc 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -28,9 +28,9 @@ * The kernel space is required not to start at address 0, which is used to * report allocation errors. */ -#if PMAP_MIN_KMEM_ADDRESS == 0 +#if PMAP_START_KMEM_ADDRESS == 0 #error "kernel space must not start at address 0" -#endif /* PMAP_MIN_KMEM_ADDRESS == 0 */ +#endif /* PMAP_START_KMEM_ADDRESS == 0 */ /* * Special kernel addresses. diff --git a/vm/vm_map.c b/vm/vm_map.c index da0b2cf..180419a 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -748,7 +748,7 @@ static int __init vm_map_bootstrap(void) { vm_map_init(kernel_map, kernel_pmap, - PMAP_MIN_KMEM_ADDRESS, PMAP_MAX_KMEM_ADDRESS); + PMAP_START_KMEM_ADDRESS, PMAP_END_KMEM_ADDRESS); kmem_cache_init(&vm_map_entry_cache, "vm_map_entry", sizeof(struct vm_map_entry), 0, NULL, KMEM_CACHE_PAGE_ONLY); @@ -792,7 +792,7 @@ vm_map_create(struct vm_map **mapp) goto error_pmap; } - vm_map_init(map, pmap, PMAP_MIN_ADDRESS, PMAP_MAX_ADDRESS); + vm_map_init(map, pmap, PMAP_START_ADDRESS, PMAP_END_ADDRESS); *mapp = map; return 0; diff --git a/vm/vm_page.h b/vm/vm_page.h index e9ca321..844ad06 100644 --- a/vm/vm_page.h +++ b/vm/vm_page.h @@ -120,15 +120,15 @@ static inline uintptr_t vm_page_direct_va(phys_addr_t pa) { assert(pa < PMEM_DIRECTMAP_LIMIT); - return ((uintptr_t)pa + PMAP_MIN_DIRECTMAP_ADDRESS); + return ((uintptr_t)pa + PMAP_START_DIRECTMAP_ADDRESS); } static inline phys_addr_t vm_page_direct_pa(uintptr_t va) { - assert(va >= PMAP_MIN_DIRECTMAP_ADDRESS); - assert(va < PMAP_MAX_DIRECTMAP_ADDRESS); - return (va - PMAP_MIN_DIRECTMAP_ADDRESS); + assert(va >= PMAP_START_DIRECTMAP_ADDRESS); + assert(va < PMAP_END_DIRECTMAP_ADDRESS); + return (va - PMAP_START_DIRECTMAP_ADDRESS); } static inline void * -- cgit v1.2.3 From ae2a752261bc95a2227c74785f11d535070696f6 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 23:43:32 +0200 Subject: test/test_mutex: minor change Add a macro expanding to the required number of processors. --- test/test_mutex.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_mutex.c b/test/test_mutex.c index f24cc9e..9e28c78 100644 --- a/test/test_mutex.c +++ b/test/test_mutex.c @@ -41,6 +41,8 @@ #include #include +#define TEST_MIN_CPUS 3 + #define TEST_REPORT_INTERVAL 10000 struct test { @@ -164,8 +166,8 @@ test_setup(void) { uint64_t time; - if (cpu_count() < 3) { - panic("test: at least 3 processors are required"); + if (cpu_count() < TEST_MIN_CPUS) { + panic("test: at least %u processors are required", TEST_MIN_CPUS); } test_create(1); -- cgit v1.2.3 From 69d73fd289e6e4b2202968a515947d6af2e90fc0 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 6 Sep 2017 23:44:11 +0200 Subject: test/test_mutex_pi: make test threads sleep Unlike the TODO description, this change only adds a sleep to make processor time available to the system, because changing as described by the TODO entry makes it much more difficult to produce the expected results. --- test/test_mutex_pi.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/test/test_mutex_pi.c b/test/test_mutex_pi.c index cfadb59..ef47048 100644 --- a/test/test_mutex_pi.c +++ b/test/test_mutex_pi.c @@ -59,9 +59,6 @@ * In order to artificially create priority inversions, all threads run on * separate processors, making this test require 5 processors. * - * TODO Use timers instead of busy-waiting so that binding to processors - * isn't required. - * * The test should output a couple of messages about thread priorities * being boosted, and then frequent updates from each thread to show * they're all making progress. Thread B suffers from contention the most @@ -90,6 +87,8 @@ #include #include +#define TEST_MIN_CPUS 5 + #define TEST_PRIO_A (THREAD_SCHED_RT_PRIO_MIN + 1) #define TEST_PRIO_B (TEST_PRIO_A + 1) #define TEST_PRIO_C (TEST_PRIO_B + 1) @@ -136,10 +135,17 @@ test_get_name(void) } static void -test_consume_cpu(void) +test_delay(void) { volatile unsigned int i; + /* + * Put the thread to sleep to make some CPU time available, and then + * busy-wait to avoid synchronizing all threads on the clock tick. + */ + + thread_delay(1, false); + for (i = 0; i < TEST_NR_CONSUME_CPU_LOOPS; i++); } @@ -233,13 +239,13 @@ test_a(void *arg) for (i = 1; /* no condition */; i++) { for (j = 0; j < TEST_NR_LOCK_LOOPS; j++) { mutex_lock(&test_mutex_1); - test_consume_cpu(); + test_delay(); test_for_priority_boosted(&highest_priority); mutex_unlock(&test_mutex_1); test_for_priority_deboosted(); - test_consume_cpu(); + test_delay(); } test_report_progress(i); @@ -254,12 +260,12 @@ test_b(void *arg) mutex_lock(&test_mutex_3); mutex_lock(&test_mutex_2); mutex_lock(&test_mutex_1); - test_consume_cpu(); + test_delay(); test_for_priority_boosted(arg); mutex_unlock(&test_mutex_1); - test_consume_cpu(); + test_delay(); mutex_unlock(&test_mutex_2); - test_consume_cpu(); + test_delay(); mutex_unlock(&test_mutex_3); /* @@ -301,7 +307,7 @@ test_manage_b(void *arg) error_check(error, "thread_create"); thread_join(thread_b); - test_consume_cpu(); + test_delay(); } printf("b:%u ", i); @@ -324,13 +330,13 @@ test_c(void *arg) for (i = 1; /* no condition */; i++) { for (j = 0; j < TEST_NR_LOCK_LOOPS; j++) { mutex_lock(&test_mutex_2); - test_consume_cpu(); + test_delay(); test_for_priority_boosted(&highest_priority); mutex_unlock(&test_mutex_2); test_for_priority_deboosted(); - test_consume_cpu(); + test_delay(); } test_report_progress(i); @@ -344,7 +350,7 @@ test_chprio_c(void *arg) thread_c = arg; - test_consume_cpu(); + test_delay(); for (;;) { thread_setscheduler(thread_c, THREAD_SCHED_POLICY_FIFO, @@ -369,13 +375,13 @@ test_d(void *arg) for (i = 1; /* no condition */; i++) { for (j = 0; j < TEST_NR_LOCK_LOOPS; j++) { mutex_lock(&test_mutex_3); - test_consume_cpu(); + test_delay(); test_for_priority_boosted(&highest_priority); mutex_unlock(&test_mutex_3); test_for_priority_deboosted(); - test_consume_cpu(); + test_delay(); } test_report_progress(i); @@ -397,13 +403,13 @@ test_e(void *arg) for (i = 1; /* no condition */; i++) { for (j = 0; j < TEST_NR_LOCK_LOOPS; j++) { mutex_lock(&test_mutex_3); - test_consume_cpu(); + test_delay(); test_for_priority_boosted(&highest_priority); mutex_unlock(&test_mutex_3); test_for_priority_deboosted(); - test_consume_cpu(); + test_delay(); } test_report_progress(i); @@ -418,6 +424,10 @@ test_setup(void) struct cpumap *cpumap; int error; + if (cpu_count() < TEST_MIN_CPUS) { + panic("test: at least %u processors are required", TEST_MIN_CPUS); + } + mutex_init(&test_mutex_1); mutex_init(&test_mutex_2); mutex_init(&test_mutex_3); -- cgit v1.2.3 From eb726b5bcbf44e053727b69aa85d86e81c7d1e65 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: Rephrase the goals of the project --- README | 4 ++-- doc/intro.9.txt | 17 ++--------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/README b/README index e1daf2a..45014ec 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ -X15 is an open source microkernel. Its purpose is to provide a foundation -for a Hurd-like operating system as well as real-time kernel applications. +X15 is an open source real-time microkernel intended to provide a performant +and scalable environment for cache-coherent multiprocessor machines. See https://www.sceen.net/x15/. diff --git a/doc/intro.9.txt b/doc/intro.9.txt index e63ae9a..dcf138a 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -12,8 +12,8 @@ intro - introduction to kernel interfaces DESCRIPTION ----------- -X15 is an open source microkernel. Its purpose is to provide a foundation -for a Hurd-like operating system as well as real-time kernel applications. +X15 is an open source real-time microkernel intended to provide a performant +and scalable environment for cache-coherent multiprocessor machines. Section 9 of the manual describes kernel interfaces, both internal and provided to application code hosted in kernel mode. @@ -26,7 +26,6 @@ Among the features provided are : * <> * <> * <> -* <> Modules ~~~~~~~ @@ -271,18 +270,6 @@ dependent loads. This model closely matches the ARM family of processors. TODO Fix memory model description -[[posix_like_interface]] -POSIX-LIKE INTERFACE --------------------- - -Many of the functions provided by the kernel match well-known POSIX -interfaces. In particular, this is true for standard integer types, -memory and string functions, and multithreading. While the thread module -doesn't comply with POSIX, it was designed so that adding a lightweight -wrapper could easily be done. Users must keep in mind that the behaviour -of some interfaces are not meant to comply with POSIX regarding certain -details; they are only meant to be POSIX-like. - SEE --- -- cgit v1.2.3 From 86aa733ad44b4f20947ab8549920a48bd997282f Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: doc/intro(9): fix machine-independent code memory model description --- doc/intro.9.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index dcf138a..903be63 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -263,12 +263,11 @@ module:arch/tcb:: module:arch/trap:: Interrupt and exception handling. -X15 currently requires a memory management unit, but that may change in -the future. In addition, the machine-independent code assumes an almost -completely relaxed memory model, but still expects no reordering between -dependent loads. This model closely matches the ARM family of processors. +The machine-independent code assumes a completely relaxed memory model as +allowed by the C11 specification. -TODO Fix memory model description +X15 currently requires a memory management unit, but that may change in +the future. SEE --- -- cgit v1.2.3 From 4febbe1c657c026a33bdb16b51a0f317217b8d5a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: kern/thread: fix thread stack guard selection --- kern/thread.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kern/thread.c b/kern/thread.c index b5a3ae4..64df194 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -288,9 +288,9 @@ static struct thread thread_booters[X15_MAX_CPUS] __initdata; static struct kmem_cache thread_cache; -#ifndef X15_THREAD_STACK_GUARD +#ifndef X15_ENABLE_THREAD_STACK_GUARD static struct kmem_cache thread_stack_cache; -#endif /* X15_THREAD_STACK_GUARD */ +#endif /* X15_ENABLE_THREAD_STACK_GUARD */ static const unsigned char thread_policy_table[THREAD_NR_SCHED_POLICIES] = { [THREAD_SCHED_POLICY_FIFO] = THREAD_SCHED_CLASS_RT, @@ -1876,7 +1876,7 @@ thread_unlock_runq(struct thread_runq *runq, unsigned long flags) spinlock_unlock_intr_restore(&runq->lock, flags); } -#ifdef X15_THREAD_STACK_GUARD +#ifdef X15_ENABLE_THREAD_STACK_GUARD #include #include @@ -1935,7 +1935,7 @@ thread_free_stack(void *stack) vm_kmem_free(va, (PAGE_SIZE * 2) + stack_size); } -#else /* X15_THREAD_STACK_GUARD */ +#else /* X15_ENABLE_THREAD_STACK_GUARD */ static void * thread_alloc_stack(void) @@ -1949,7 +1949,7 @@ thread_free_stack(void *stack) kmem_cache_free(&thread_stack_cache, stack); } -#endif /* X15_THREAD_STACK_GUARD */ +#endif /* X15_ENABLE_THREAD_STACK_GUARD */ static void thread_destroy(struct thread *thread) @@ -2284,10 +2284,10 @@ thread_setup(void) kmem_cache_init(&thread_cache, "thread", sizeof(struct thread), CPU_L1_SIZE, NULL, 0); -#ifndef X15_THREAD_STACK_GUARD +#ifndef X15_ENABLE_THREAD_STACK_GUARD kmem_cache_init(&thread_stack_cache, "thread_stack", TCB_STACK_SIZE, CPU_DATA_ALIGN, NULL, 0); -#endif /* X15_THREAD_STACK_GUARD */ +#endif /* X15_ENABLE_THREAD_STACK_GUARD */ cpumap_for_each(&thread_active_runqs, cpu) { thread_setup_runq(percpu_ptr(thread_runq, cpu)); @@ -2304,7 +2304,7 @@ INIT_OP_DEFINE(thread_setup, INIT_OP_DEP(task_setup, true), INIT_OP_DEP(thread_bootstrap, true), INIT_OP_DEP(turnstile_setup, true), -#ifdef X15_THREAD_STACK_GUARD +#ifdef X15_ENABLE_THREAD_STACK_GUARD INIT_OP_DEP(vm_kmem_setup, true), INIT_OP_DEP(vm_map_setup, true), INIT_OP_DEP(vm_page_setup, true), -- cgit v1.2.3 From 888853dc07f1cdb9701f3612dea7828cc96b84cc Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: Use accessors when referring to global kernel objects The kernel_map/kernel_pmap/kernel_task/etc... names were reused as they were in the Mach source code. They've been a (mostly harmless) long-standing violation of the coding rules. --- arch/x86/machine/pmap.c | 41 ++++++++++++++++++++++------------------- arch/x86/machine/pmap.h | 11 +++++++---- kern/task.c | 12 +++++------- kern/task.h | 11 +++++++---- kern/thread.c | 6 +++++- test/test_vm_page_fill.c | 10 ++++++++++ vm/vm_kmem.c | 31 +++++++++++++++++-------------- vm/vm_kmem.h | 5 ----- vm/vm_map.c | 6 ++++-- vm/vm_map.h | 8 ++++++++ vm/vm_object.c | 2 ++ vm/vm_object.h | 8 ++++++++ 12 files changed, 95 insertions(+), 56 deletions(-) diff --git a/arch/x86/machine/pmap.c b/arch/x86/machine/pmap.c index fb251b0..07b8a70 100644 --- a/arch/x86/machine/pmap.c +++ b/arch/x86/machine/pmap.c @@ -93,15 +93,14 @@ struct pmap { typedef void (*pmap_walk_fn_t)(phys_addr_t pa, unsigned int index, unsigned int level); -static struct pmap kernel_pmap_store __read_mostly; -struct pmap *kernel_pmap __read_mostly = &kernel_pmap_store; - /* * The kernel per-CPU page tables are used early enough during bootstrap * that using a percpu variable would actually become ugly. This array * is rather small anyway. */ -static struct pmap_cpu_table kernel_pmap_cpu_tables[X15_MAX_CPUS] __read_mostly; +static struct pmap_cpu_table pmap_kernel_cpu_tables[X15_MAX_CPUS] __read_mostly; + +struct pmap pmap_kernel_pmap; struct pmap *pmap_current_ptr __percpu; @@ -478,7 +477,7 @@ pmap_setup_paging(void) } #endif /* __LP64__ */ - cpu_table = (void *)BOOT_VTOP((uintptr_t)&kernel_pmap_cpu_tables[0]); + cpu_table = (void *)BOOT_VTOP((uintptr_t)&pmap_kernel_cpu_tables[0]); cpu_table->root_ptp_pa = (uintptr_t)root_ptp; return root_ptp; @@ -494,7 +493,7 @@ pmap_ap_setup_paging(void) pgsize = pmap_boot_get_pgsize(); pmap_boot_enable_pgext(pgsize); - pmap = (void *)BOOT_VTOP((uintptr_t)&kernel_pmap_store); + pmap = (void *)BOOT_VTOP((uintptr_t)&pmap_kernel_pmap); cpu_table = (void *)BOOT_VTOP((uintptr_t)pmap->cpu_tables[boot_ap_id]); #ifdef X15_X86_PAE @@ -513,7 +512,7 @@ MACRO_BEGIN \ assert(((end) <= PMAP_START_DIRECTMAP_ADDRESS) \ || ((start) >= PMAP_END_DIRECTMAP_ADDRESS)); \ \ - if ((pmap) == kernel_pmap) { \ + if ((pmap) == pmap_get_kernel_pmap()) { \ assert(((start) >= PMAP_START_KMEM_ADDRESS) \ && ((end) <= PMAP_END_KMEM_ADDRESS)); \ } else { \ @@ -588,7 +587,7 @@ pmap_walk_vas(uintptr_t start, uintptr_t end, pmap_walk_fn_t walk_fn) #endif /* __LP64__ */ va = start; - root_ptp_pa = kernel_pmap->cpu_tables[cpu_id()]->root_ptp_pa; + root_ptp_pa = pmap_get_kernel_pmap()->cpu_tables[cpu_id()]->root_ptp_pa; do { #ifdef __LP64__ @@ -830,12 +829,12 @@ pmap_bootstrap(void) struct pmap_cpu_table *cpu_table; unsigned int i; - for (i = 0; i < ARRAY_SIZE(kernel_pmap->cpu_tables); i++) { - cpu_table = &kernel_pmap_cpu_tables[i]; - kernel_pmap->cpu_tables[i] = cpu_table; + for (i = 0; i < ARRAY_SIZE(pmap_get_kernel_pmap()->cpu_tables); i++) { + cpu_table = &pmap_kernel_cpu_tables[i]; + pmap_get_kernel_pmap()->cpu_tables[i] = cpu_table; } - cpu_local_assign(pmap_current_ptr, kernel_pmap); + cpu_local_assign(pmap_current_ptr, pmap_get_kernel_pmap()); pmap_prot_table[VM_PROT_NONE] = 0; pmap_prot_table[VM_PROT_READ] = 0; @@ -918,7 +917,7 @@ INIT_OP_DEFINE(pmap_setup, void __init pmap_ap_setup(void) { - cpu_local_assign(pmap_current_ptr, kernel_pmap); + cpu_local_assign(pmap_current_ptr, pmap_get_kernel_pmap()); if (cpu_has_global_pages()) { cpu_enable_global_pages(); @@ -993,14 +992,16 @@ static void __init pmap_copy_cpu_table(unsigned int cpu) { struct pmap_cpu_table *cpu_table; + struct pmap *kernel_pmap; unsigned int level; const pmap_pte_t *sptp; pmap_pte_t *dptp; assert(cpu != 0); - cpu_table = kernel_pmap->cpu_tables[cpu]; + cpu_table = pmap_get_kernel_pmap()->cpu_tables[cpu]; level = PMAP_NR_LEVELS - 1; + kernel_pmap = pmap_get_kernel_pmap(); sptp = pmap_ptp_from_pa(kernel_pmap->cpu_tables[cpu_id()]->root_ptp_pa); #ifdef X15_X86_PAE @@ -1107,10 +1108,12 @@ int pmap_kextract(uintptr_t va, phys_addr_t *pap) { const struct pmap_pt_level *pt_level; + struct pmap *kernel_pmap; pmap_pte_t *ptp, *pte; unsigned int level; level = PMAP_NR_LEVELS - 1; + kernel_pmap = pmap_get_kernel_pmap(); ptp = pmap_ptp_from_pa(kernel_pmap->cpu_tables[cpu_id()]->root_ptp_pa); for (;;) { @@ -1168,7 +1171,7 @@ pmap_enter_local(struct pmap *pmap, uintptr_t va, phys_addr_t pa, pte_bits = PMAP_PTE_RW; - if (pmap != kernel_pmap) { + if (pmap != pmap_get_kernel_pmap()) { pte_bits |= PMAP_PTE_US; } @@ -1203,7 +1206,7 @@ pmap_enter_local(struct pmap *pmap, uintptr_t va, phys_addr_t pa, } assert(!pmap_pte_valid(*pte)); - pte_bits = ((pmap == kernel_pmap) ? PMAP_PTE_G : PMAP_PTE_US) + pte_bits = ((pmap == pmap_get_kernel_pmap()) ? PMAP_PTE_G : PMAP_PTE_US) | pmap_prot_table[prot & VM_PROT_ALL]; pmap_pte_set(pte, pa, pte_bits, pt_level); return 0; @@ -1377,7 +1380,7 @@ pmap_protect(struct pmap *pmap, uintptr_t va, int prot, static void pmap_flush_tlb(struct pmap *pmap, uintptr_t start, uintptr_t end) { - if ((pmap != pmap_current()) && (pmap != kernel_pmap)) { + if ((pmap != pmap_current()) && (pmap != pmap_get_kernel_pmap())) { return; } @@ -1390,11 +1393,11 @@ pmap_flush_tlb(struct pmap *pmap, uintptr_t start, uintptr_t end) static void pmap_flush_tlb_all(struct pmap *pmap) { - if ((pmap != pmap_current()) && (pmap != kernel_pmap)) { + if ((pmap != pmap_current()) && (pmap != pmap_get_kernel_pmap())) { return; } - if (pmap == kernel_pmap) { + if (pmap == pmap_get_kernel_pmap()) { cpu_tlb_flush_all(); } else { cpu_tlb_flush(); diff --git a/arch/x86/machine/pmap.h b/arch/x86/machine/pmap.h index 96fc21e..d8c2455 100644 --- a/arch/x86/machine/pmap.h +++ b/arch/x86/machine/pmap.h @@ -183,10 +183,13 @@ typedef phys_addr_t pmap_pte_t; */ struct pmap; -/* - * The kernel pmap. - */ -extern struct pmap *kernel_pmap; +static inline struct pmap * +pmap_get_kernel_pmap(void) +{ + extern struct pmap pmap_kernel_pmap; + + return &pmap_kernel_pmap; +} /* * Early initialization of the MMU. diff --git a/kern/task.c b/kern/task.c index 1621056..35220d3 100644 --- a/kern/task.c +++ b/kern/task.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #ifdef __LP64__ @@ -37,11 +36,7 @@ #define TASK_INFO_ADDR_FMT "%08lx" #endif /* __LP64__ */ -/* - * Kernel task and storage. - */ -static struct task kernel_task_store; -struct task *kernel_task __read_mostly = &kernel_task_store; +struct task task_kernel_task; /* * Cache for allocated tasks. @@ -117,10 +112,13 @@ INIT_OP_DEFINE(task_setup_shell, static int __init task_setup(void) { + struct task *kernel_task; + + kernel_task = task_get_kernel_task(); kmem_cache_init(&task_cache, "task", sizeof(struct task), 0, NULL, 0); list_init(&task_list); spinlock_init(&task_list_lock); - task_init(kernel_task, "x15", kernel_map); + task_init(kernel_task, "x15", vm_map_get_kernel_map()); list_insert_head(&task_list, &kernel_task->node); return 0; } diff --git a/kern/task.h b/kern/task.h index 149ff49..4573979 100644 --- a/kern/task.h +++ b/kern/task.h @@ -43,10 +43,13 @@ struct task { char name[TASK_NAME_SIZE]; }; -/* - * The kernel task. - */ -extern struct task *kernel_task; +static inline struct task * +task_get_kernel_task(void) +{ + extern struct task task_kernel_task; + + return &task_kernel_task; +} static inline void task_ref(struct task *task) diff --git a/kern/thread.c b/kern/thread.c index 64df194..f9c2274 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -116,6 +116,7 @@ #include #include #include +#include #include /* @@ -1693,7 +1694,7 @@ thread_init_booter(unsigned int cpu) thread_set_user_priority(booter, 0); thread_reset_real_priority(booter); memset(booter->tsd, 0, sizeof(booter->tsd)); - booter->task = kernel_task; + booter->task = task_get_kernel_task(); snprintf(booter->name, sizeof(booter->name), THREAD_KERNEL_PREFIX "thread_boot/%u", cpu); } @@ -1887,11 +1888,14 @@ thread_alloc_stack(void) { __unused struct vm_page *first_page, *last_page; phys_addr_t first_pa, last_pa; + struct pmap *kernel_pmap; size_t stack_size; uintptr_t va; void *mem; __unused int error; + kernel_pmap = pmap_get_kernel_pmap(); + stack_size = vm_page_round(TCB_STACK_SIZE); mem = vm_kmem_alloc((PAGE_SIZE * 2) + stack_size); diff --git a/test/test_vm_page_fill.c b/test/test_vm_page_fill.c index 2b7f113..0b4278b 100644 --- a/test/test_vm_page_fill.c +++ b/test/test_vm_page_fill.c @@ -45,10 +45,15 @@ static unsigned char test_pattern = 1; static void test_write_pages(void) { + struct vm_map *kernel_map; + struct pmap *kernel_pmap; struct vm_page *page; int error, flags; uintptr_t va; + kernel_map = vm_map_get_kernel_map(); + kernel_pmap = pmap_get_kernel_pmap(); + for (;;) { page = vm_page_alloc(0, VM_PAGE_SEL_HIGHMEM, VM_PAGE_KERNEL); @@ -80,10 +85,15 @@ test_write_pages(void) static void test_reset_pages(void) { + struct vm_map *kernel_map; + struct pmap *kernel_pmap; struct vm_page *page; int error, flags; uintptr_t va; + kernel_map = vm_map_get_kernel_map(); + kernel_pmap = pmap_get_kernel_pmap(); + while (!list_empty(&test_pages)) { page = list_first_entry(&test_pages, struct vm_page, node); list_remove(&page->node); diff --git a/vm/vm_kmem.c b/vm/vm_kmem.c index 0583bb9..c0a91f1 100644 --- a/vm/vm_kmem.c +++ b/vm/vm_kmem.c @@ -33,14 +33,6 @@ #include #include -/* - * Kernel map and storage. - */ -static struct vm_map kernel_map_store; -struct vm_map *kernel_map __read_mostly = &kernel_map_store; - -static struct vm_object vm_kmem_kernel_object; - static uint64_t vm_kmem_offset(uintptr_t va) { @@ -54,7 +46,7 @@ vm_kmem_setup(void) uint64_t size; size = vm_kmem_offset(PMAP_END_KMEM_ADDRESS); - vm_object_init(&vm_kmem_kernel_object, size); + vm_object_init(vm_object_get_kernel_object(), size); return 0; } @@ -96,7 +88,7 @@ vm_kmem_alloc_va(size_t size) va = 0; flags = VM_MAP_FLAGS(VM_PROT_ALL, VM_PROT_ALL, VM_INHERIT_NONE, VM_ADV_DEFAULT, 0); - error = vm_map_enter(kernel_map, &va, size, 0, flags, NULL, 0); + error = vm_map_enter(vm_map_get_kernel_map(), &va, size, 0, flags, NULL, 0); if (error) { return NULL; @@ -112,12 +104,14 @@ vm_kmem_free_va(void *addr, size_t size) va = (uintptr_t)addr; assert(vm_kmem_free_check(va, size) == 0); - vm_map_remove(kernel_map, va, va + vm_page_round(size)); + vm_map_remove(vm_map_get_kernel_map(), va, va + vm_page_round(size)); } void * vm_kmem_alloc(size_t size) { + struct vm_object *kernel_object; + struct pmap *kernel_pmap; struct vm_page *page; uintptr_t va, start, end; int error; @@ -129,6 +123,9 @@ vm_kmem_alloc(size_t size) return NULL; } + kernel_object = vm_object_get_kernel_object(); + kernel_pmap = pmap_get_kernel_pmap(); + for (start = va, end = va + size; start < end; start += PAGE_SIZE) { page = vm_page_alloc(0, VM_PAGE_SEL_HIGHMEM, VM_PAGE_KERNEL); @@ -140,8 +137,7 @@ vm_kmem_alloc(size_t size) * The page becomes managed by the object and is freed in case * of failure. */ - error = vm_object_insert(&vm_kmem_kernel_object, page, - vm_kmem_offset(start)); + error = vm_object_insert(kernel_object, page, vm_kmem_offset(start)); if (error) { goto error; @@ -172,12 +168,14 @@ void vm_kmem_free(void *addr, size_t size) { const struct cpumap *cpumap; + struct pmap *kernel_pmap; uintptr_t va, end; va = (uintptr_t)addr; size = vm_page_round(size); end = va + size; cpumap = cpumap_all(); + kernel_pmap = pmap_get_kernel_pmap(); while (va < end) { pmap_remove(kernel_pmap, va, cpumap); @@ -185,7 +183,7 @@ vm_kmem_free(void *addr, size_t size) } pmap_update(kernel_pmap); - vm_object_remove(&vm_kmem_kernel_object, + vm_object_remove(vm_object_get_kernel_object(), vm_kmem_offset((uintptr_t)addr), vm_kmem_offset(end)); vm_kmem_free_va(addr, size); @@ -195,11 +193,14 @@ void * vm_kmem_map_pa(phys_addr_t pa, size_t size, uintptr_t *map_vap, size_t *map_sizep) { + struct pmap *kernel_pmap; uintptr_t offset, map_va; size_t map_size; phys_addr_t start; int error; + kernel_pmap = pmap_get_kernel_pmap(); + start = vm_page_trunc(pa); map_size = vm_page_round(pa + size) - start; map_va = (uintptr_t)vm_kmem_alloc_va(map_size); @@ -242,9 +243,11 @@ void vm_kmem_unmap_pa(uintptr_t map_va, size_t map_size) { const struct cpumap *cpumap; + struct pmap *kernel_pmap; uintptr_t va, end; cpumap = cpumap_all(); + kernel_pmap = pmap_get_kernel_pmap(); end = map_va + map_size; for (va = map_va; va < end; va += PAGE_SIZE) { diff --git a/vm/vm_kmem.h b/vm/vm_kmem.h index 9bb90dc..5ae4a16 100644 --- a/vm/vm_kmem.h +++ b/vm/vm_kmem.h @@ -40,11 +40,6 @@ extern char _rodata; extern char _data; extern char _end; -/* - * The kernel map. - */ -extern struct vm_map *kernel_map; - /* * Allocate pure virtual kernel pages. * diff --git a/vm/vm_map.c b/vm/vm_map.c index 180419a..713d92f 100644 --- a/vm/vm_map.c +++ b/vm/vm_map.c @@ -76,6 +76,8 @@ static int vm_map_insert(struct vm_map *map, struct vm_map_entry *entry, static struct kmem_cache vm_map_entry_cache; static struct kmem_cache vm_map_cache; +struct vm_map vm_map_kernel_map; + static struct vm_map_entry * vm_map_entry_create(void) { @@ -747,7 +749,7 @@ INIT_OP_DEFINE(vm_map_setup_shell, static int __init vm_map_bootstrap(void) { - vm_map_init(kernel_map, kernel_pmap, + vm_map_init(vm_map_get_kernel_map(), pmap_get_kernel_pmap(), PMAP_START_KMEM_ADDRESS, PMAP_END_KMEM_ADDRESS); kmem_cache_init(&vm_map_entry_cache, "vm_map_entry", sizeof(struct vm_map_entry), 0, NULL, @@ -808,7 +810,7 @@ vm_map_info(struct vm_map *map) struct vm_map_entry *entry; const char *type, *name; - if (map == kernel_map) { + if (map == vm_map_get_kernel_map()) { name = "kernel map"; } else { name = "map"; diff --git a/vm/vm_map.h b/vm/vm_map.h index 653eabd..009c746 100644 --- a/vm/vm_map.h +++ b/vm/vm_map.h @@ -92,6 +92,14 @@ struct vm_map { struct pmap *pmap; }; +static inline struct vm_map * +vm_map_get_kernel_map(void) +{ + extern struct vm_map vm_map_kernel_map; + + return &vm_map_kernel_map; +} + /* * Create a virtual mapping. */ diff --git a/vm/vm_object.c b/vm/vm_object.c index 679f353..707008e 100644 --- a/vm/vm_object.c +++ b/vm/vm_object.c @@ -31,6 +31,8 @@ #include #include +struct vm_object vm_object_kernel_object; + static int __init vm_object_setup(void) { diff --git a/vm/vm_object.h b/vm/vm_object.h index 9ffe711..2f0e8e1 100644 --- a/vm/vm_object.h +++ b/vm/vm_object.h @@ -33,6 +33,14 @@ struct vm_object; +static inline struct vm_object * +vm_object_get_kernel_object(void) +{ + extern struct vm_object vm_object_kernel_object; + + return &vm_object_kernel_object; +} + /* * Initialize a VM object. */ -- cgit v1.2.3 From 2ab04b9af9dbc565192dc63b88ed0fd1a42d2082 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: kern/macros: new __used attribute macro --- kern/macros.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kern/macros.h b/kern/macros.h index 83bab87..d09eca4 100644 --- a/kern/macros.h +++ b/kern/macros.h @@ -102,4 +102,8 @@ moo_print(const char *s) #define __unused __attribute__((unused)) #endif +#ifndef __used +#define __used __attribute__((used)) +#endif + #endif /* _KERN_MACROS_H */ -- cgit v1.2.3 From 44d0759b9a2d6cf715cc45442ad096d5bd8d9fa4 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: kern/init: fix LTO builds Initialization operations are never directly referenced, as they're retrieved by accessing the .init.ops subsection. As a result, whole program optimizations as applied during link time optimizations cause the structures and most of the related code not to be compiled in. Use the recently added __used attribute macro to fix this issue. --- kern/init.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/init.h b/kern/init.h index 8f1cacf..7c3522e 100644 --- a/kern/init.h +++ b/kern/init.h @@ -90,7 +90,7 @@ typedef int (*init_op_fn_t)(void); __VA_ARGS__ \ }; \ \ - struct init_op INIT_OP(_fn) __initop = { \ + struct init_op INIT_OP(_fn) __initop __used = { \ .name = QUOTE(_fn), \ .fn = _fn, \ .deps = INIT_OP_DEPS(_fn), \ -- cgit v1.2.3 From 63838cf0f495bd18e166499ce1b99f4bf5c26fa5 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: kern/semaphore: add missing atomic access --- kern/semaphore_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kern/semaphore_i.h b/kern/semaphore_i.h index 6e79b13..21f76cf 100644 --- a/kern/semaphore_i.h +++ b/kern/semaphore_i.h @@ -33,7 +33,7 @@ semaphore_dec(struct semaphore *semaphore) unsigned int prev, value; do { - value = semaphore->value; + value = atomic_load(&semaphore->value, ATOMIC_RELAXED); if (value == 0) { break; -- cgit v1.2.3 From 652096292732e1ca10d22a3dfaa24af098956c6a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 7 Sep 2017 20:59:07 +0200 Subject: kern/semaphore: do not assume common case Unlike locks, which are expected to be used with the goal of minimum contention in mind, semaphores usage is more vague and it's not reasonable to expect a common case where they're unlocked. --- kern/semaphore.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kern/semaphore.h b/kern/semaphore.h index e1acbf2..9de5d4a 100644 --- a/kern/semaphore.h +++ b/kern/semaphore.h @@ -37,7 +37,6 @@ #include #include -#include #define SEMAPHORE_VALUE_MAX 32768 @@ -69,7 +68,7 @@ semaphore_trywait(struct semaphore *semaphore) prev = semaphore_dec(semaphore); - if (unlikely(prev == 0)) { + if (prev == 0) { return ERROR_AGAIN; } @@ -89,7 +88,7 @@ semaphore_wait(struct semaphore *semaphore) prev = semaphore_dec(semaphore); - if (unlikely(prev == 0)) { + if (prev == 0) { semaphore_wait_slow(semaphore); } } @@ -101,7 +100,7 @@ semaphore_timedwait(struct semaphore *semaphore, uint64_t ticks) prev = semaphore_dec(semaphore); - if (unlikely(prev == 0)) { + if (prev == 0) { return semaphore_timedwait_slow(semaphore, ticks); } @@ -123,7 +122,7 @@ semaphore_post(struct semaphore *semaphore) prev = semaphore_inc(semaphore); - if (unlikely(prev == 0)) { + if (prev == 0) { semaphore_post_slow(semaphore); } } -- cgit v1.2.3 From 906770cbe8fe9cec5c333e9fc87b7d47ecccb153 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 8 Sep 2017 00:16:30 +0200 Subject: doc/cenv(9): describe the C environment of the kernel --- doc/Makefrag.am | 1 + doc/asciidoc.conf | 9 ++++++ doc/cenv.9.txt | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/intro.9.txt | 12 +++---- 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 doc/cenv.9.txt diff --git a/doc/Makefrag.am b/doc/Makefrag.am index 86c468c..4f9083d 100644 --- a/doc/Makefrag.am +++ b/doc/Makefrag.am @@ -1,6 +1,7 @@ EXTRA_DIST += doc/asciidoc.conf x15_DOCS = \ + doc/cenv.9.txt \ doc/init.9.txt \ doc/intro.9.txt \ doc/style.9.txt diff --git a/doc/asciidoc.conf b/doc/asciidoc.conf index a413768..6e0874c 100644 --- a/doc/asciidoc.conf +++ b/doc/asciidoc.conf @@ -9,6 +9,7 @@ the-art-of-unix-programming=http://www.catb.org/esr/writings/taoup/html/[The Art (?su)(?Pmodule_mutex):(?P\w+)= (?su)(?Pmodule):(?P\w+)/(?P\w+)= (?su)(?Pmanpage):(?P\w+)= +(?su)(?Pcheader):(?P
\w+)= (?su)(?Pcfunction):(?P\w+)= (?su)(?Pcmacro):(?P\w+)= (?su)(?Pshell):{(?P[^}]+)}= @@ -37,6 +38,14 @@ ifdef::basebackend-docbook[] {page}(9) endif::basebackend-docbook[] +[cheader-inlinemacro] +ifdef::basebackend-html[] +<{header}.h> +endif::basebackend-html[] +ifdef::basebackend-docbook[] +<{header}.h> +endif::basebackend-docbook[] + [cfunction-inlinemacro] ifdef::basebackend-html[] {function}() diff --git a/doc/cenv.9.txt b/doc/cenv.9.txt new file mode 100644 index 0000000..693d813 --- /dev/null +++ b/doc/cenv.9.txt @@ -0,0 +1,93 @@ +CENV(9) +======== +:doctype: manpage +:man source: X15 +:man manual: X15 C environment + +NAME +---- + +cenv - C environment + +DESCRIPTION +----------- + +This document describes the C environment of the kernel, including +available standard headers and interfaces. + +Standard +~~~~~~~~ + +X15 must be built with a C compiler supporting the ISO/IEC 9899:2011 +(C11) specification with some GCC extensions. It can currently be built +with the GCC and Clang compilers. + +Environment +~~~~~~~~~~~ + +The kernel uses the freestanding execution environment. This means the +compiler is expected to provide the following headers : + +** cheader:float +** cheader:iso646 +** cheader:limits +** cheader:stdalign +** cheader:stdarg +** cheader:stdbool +** cheader:stddef +** cheader:stdint +** cheader:stdnoreturn + +X15 augments the environment with a small subset of the functions provided +in hosted environments. The additional headers are : + +** cheader:assert +** cheader:limits +** cheader:stdio +** cheader:string + +Note that these headers do not provide the complete set of interfaces +expected in a hosted environment. + +Formatting functions +~~~~~~~~~~~~~~~~~~~~ + +Of particular interest is the cheader:stdio header which provides a subset +of the printf and scanf family of functions. + +The supported functions are : + +** cfunction:getchar +** cfunction:putchar +** cfunction:sprintf +** cfunction:snprintf +** cfunction:vsprintf +** cfunction:vsnprintf +** cfunction:sscanf +** cfunction:vsscanf + +The *EOF* macro is also provided. + +The formatting functions only implement a subset of what the standard +defines, namely : + +** cfunction:sprintf : +*** flags: # 0 - ' ' (space) + +*** field width is supported +*** precision is supported +** cfunction:sscanf : +*** flags: * +*** field width is supported +** common to both : +*** modifiers: hh h l ll z t +*** specifiers: d i o u x X c s p n % + +Floating point conversions are currently not supported, but this may change +in the future. + +SEE +--- + +manpage:intro + +{x15-operating-system} diff --git a/doc/intro.9.txt b/doc/intro.9.txt index 903be63..e023d86 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -151,16 +151,12 @@ module:kern/list:: Doubly-linked list. module:kern/macros:: Useful generic macros. -module:kern/printf:: - Formatted output functions. module:kern/rbtree:: Red-black tree. module:kern/rdxtree:: Radix tree (with integer keys). module:kern/slist:: Singly-linked list. -module:kern/sprintf:: - Formatted string functions. module:kern/syscnt:: Generic 64-bits counter. module:kern/timer:: @@ -171,8 +167,8 @@ often vary too much. Similarly, it doesn't provide a hash table interface. Instead, users can easily build specialized queues, hash tables and ring buffers on top of the provided facilities. -TODO List supported standard C interfaces and remove the printf/sprintf -modules from the list above. +See manpage:cenv for information about the subset of standard C interfaces +supported. [[multiprocessor_support]] MULTIPROCESSOR SUPPORT @@ -272,8 +268,10 @@ the future. SEE --- -manpage:style +manpage:cenv manpage:init +manpage:style + {x15-operating-system} -- cgit v1.2.3 From 7182b09c4994671bc0c32d0041463a761a577431 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Fri, 8 Sep 2017 20:17:01 +0200 Subject: doc/intro(9): fix claim about smp scalability --- doc/intro.9.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/intro.9.txt b/doc/intro.9.txt index e023d86..243b054 100644 --- a/doc/intro.9.txt +++ b/doc/intro.9.txt @@ -175,7 +175,7 @@ MULTIPROCESSOR SUPPORT ---------------------- The X15 kernel is designed to support hardware with multiple processors. -The scheduler should scale well up to one hundred processors, with one +The scheduler should scale well up to 16-32 processors, with one run queue per processor. Threads can be bound to a specific set of processors, or temporarily pinned for short durations. Non real-time threads can be spontaneously migrated between processors in order to -- cgit v1.2.3 From c099a60b4dc284644ffe91fde60ba51175fa97b9 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 20 Sep 2017 20:21:55 +0200 Subject: x86/ssp: fix LTO builds --- arch/x86/machine/ssp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/machine/ssp.c b/arch/x86/machine/ssp.c index 77d5a2b..747f3b3 100644 --- a/arch/x86/machine/ssp.c +++ b/arch/x86/machine/ssp.c @@ -21,10 +21,10 @@ void ssp_panic(void); -void +__used void ssp_panic(void) { panic("ssp: stack corruption detected"); } -void __stack_chk_fail(void) __attribute__((alias("ssp_panic"))); +__used void __stack_chk_fail(void) __attribute__((alias("ssp_panic"))); -- cgit v1.2.3 From e72aa3bba2ab52c4435386e552028ff32ec297af Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 20 Sep 2017 20:21:55 +0200 Subject: kern/work: fix work_setup dependencies --- kern/work.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kern/work.c b/kern/work.c index ea3eb1f..59d05ec 100644 --- a/kern/work.c +++ b/kern/work.c @@ -497,12 +497,13 @@ work_setup(void) INIT_OP_DEFINE(work_setup, INIT_OP_DEP(cpu_mp_probe, true), + INIT_OP_DEP(cpumap_setup, true), INIT_OP_DEP(kmem_setup, true), INIT_OP_DEP(log_setup, true), INIT_OP_DEP(panic_setup, true), INIT_OP_DEP(spinlock_setup, true), INIT_OP_DEP(syscnt_setup, true), - INIT_OP_DEP(thread_bootstrap, true)); + INIT_OP_DEP(thread_setup, true)); void work_schedule(struct work *work, int flags) -- cgit v1.2.3 From 9b1d0f9488c905decaac5269886b137bcd23f416 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Wed, 20 Sep 2017 20:25:31 +0200 Subject: kern/thread: restore comment about thread destruction --- kern/thread.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kern/thread.c b/kern/thread.c index f9c2274..af11dba 100644 --- a/kern/thread.c +++ b/kern/thread.c @@ -2402,6 +2402,11 @@ thread_exit(void) work_schedule(&zombie.work, 0); } + /* + * Disable preemption before dropping the reference, as this may + * trigger the active state poll of the join operation. Doing so + * keeps the duration of that active wait minimum. + */ thread_preempt_disable(); thread_unref(thread); -- cgit v1.2.3 From b3b0b4f49de2447ed077e3b62368a4b68a983e0a Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 21 Sep 2017 01:17:08 +0200 Subject: Remove autotools files --- .gitignore | 11 -- INSTALL | 274 ------------------------------------ Makefile.am | 69 --------- Makefrag.am | 192 ------------------------- arch/x86/Makefrag.am | 79 ----------- arch/x86/configfrag.ac | 47 ------- configure.ac | 177 ----------------------- doc/Makefrag.am | 33 ----- tools/m4/ax_append_compile_flags.m4 | 67 --------- tools/m4/ax_append_flag.m4 | 71 ---------- tools/m4/ax_check_compile_flag.m4 | 74 ---------- tools/m4/ax_prog_a2x.m4 | 46 ------ tools/m4/ax_prog_asciidoc.m4 | 46 ------ tools/m4/ax_prog_highlight.m4 | 46 ------ tools/m4/ax_require_defined.m4 | 37 ----- 15 files changed, 1269 deletions(-) delete mode 100644 INSTALL delete mode 100644 Makefile.am delete mode 100644 Makefrag.am delete mode 100644 arch/x86/Makefrag.am delete mode 100644 arch/x86/configfrag.ac delete mode 100644 configure.ac delete mode 100644 doc/Makefrag.am delete mode 100644 tools/m4/ax_append_compile_flags.m4 delete mode 100644 tools/m4/ax_append_flag.m4 delete mode 100644 tools/m4/ax_check_compile_flag.m4 delete mode 100644 tools/m4/ax_prog_a2x.m4 delete mode 100644 tools/m4/ax_prog_asciidoc.m4 delete mode 100644 tools/m4/ax_prog_highlight.m4 delete mode 100644 tools/m4/ax_require_defined.m4 diff --git a/.gitignore b/.gitignore index 265e3aa..61d2f51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,7 @@ *.o -*.a -.deps/ -.dirstamp -Makefile -Makefile.in cscope.out tags x15.lds -/aclocal.m4 -/autom4te.cache/ -/build-aux/ -/config.* -/configure -/stamp-h1 /x15 /x15.sorted_init_ops /doc/*\.[1-9] diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 4dec35a..0000000 --- a/INSTALL +++ /dev/null @@ -1,274 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006 Free Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - -Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - - -X15 Options -=========== - -`--enable-64bits' - Build for a 64-bits target processor. - -`--enable-shell' - Enable the diagnostics shell. Note that some shell commands may have - a real-time unsafe behaviour. - -`--enable-test-module=TEST_MODULE' - Run a test module instead of booting the system. Test module source - files can be found in the test directory, and test module names - inferred from their file name. For example, a test module implemented - in a file named `test_pmap_update_mp.c' would be selected with the - option `--enable-test-module=pmap_update_mp'. - -`--enable-thread-stack-guard' - Enable the use of guard pages around kernel thread stacks to catch - overflows. Note that this feature wastes precious kernel virtual - memory and has some overhead during thread creation and destruction. - -`--with-clock-freq=CLOCK_FREQ - Set the low resolution clock frequency. - -`--with-max-cpus=MAX_CPUS' - Set the maximum number of supported processors. - -`--with-mutex-impl=MUTEX_IMPL' - Select the mutex implementation (adaptive, pi, or plain) - -TODO Improve descriptions - -x86 options ------------ - -`--enable-x86-pae' - Use the PAE (Physical Address Extension) processor feature to address - physical memory beyond 4 GiB (i386 only). diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index efff4f3..0000000 --- a/Makefile.am +++ /dev/null @@ -1,69 +0,0 @@ -EXTRA_DIST = -MOSTLYCLEANFILES = -AM_CPPFLAGS = -AM_CFLAGS = -SUFFIXES = - -AM_CPPFLAGS += -pipe -AM_CPPFLAGS += -imacros config.h - -# Do not include headers from the hosted environment, but -# do include headers from the compiler. -AM_CPPFLAGS += -nostdinc -AM_CPPFLAGS += -isystem $(shell $(CC) -print-file-name=include) - -AM_CPPFLAGS += \ - -I$(top_srcdir) \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/arch/$(arch) - -AM_CFLAGS += -ffreestanding - -AM_CFLAGS += -std=gnu11 - -AM_CFLAGS += \ - -Wall \ - -Wextra \ - -Wshadow \ - -Wmissing-prototypes \ - -Wstrict-prototypes - -AM_CFLAGS += \ - -fsigned-char \ - -fno-common - -AM_CFLAGS += -nostdlib - -SUFFIXES += .lds .lds.S - -.lds.S.lds: - $(AM_V_GEN)$(CPP) -P $(AM_CPPFLAGS) -o $@ $< - -ASCIIDOC_FLAGS = -a toc -d manpage -a revnumber=@PACKAGE_VERSION@ -A2X_FLAGS = -d manpage -a revnumber=@PACKAGE_VERSION@ - -SUFFIXES += .9 .9.html .9.xml .9.txt - -.9.txt.9.html: - $(AM_V_GEN)$(ASCIIDOC) $(ASCIIDOC_FLAGS) -b html5 -o $(abs_top_builddir)/$@ $< - -# Expose intermediate DocBook files for correct parallel builds. -.9.txt.9.xml: - $(AM_V_GEN)$(ASCIIDOC) $(ASCIIDOC_FLAGS) -b docbook -o $(abs_top_builddir)/$@ $< - -.9.xml.9: - $(AM_V_GEN)$(A2X) $(A2X_FLAGS) -f manpage --xsltproc-opts="-o $(abs_top_builddir)/$@" $< - -bin_PROGRAMS = x15 -x15_DEPENDENCIES = arch/$(arch)/x15.lds x15.sorted_init_ops -MOSTLYCLEANFILES += $(x15_DEPENDENCIES) -x15_SOURCES = -x15_LDFLAGS = -nostdlib -Xlinker -T arch/$(arch)/x15.lds -x15_LDADD = -lgcc - -.INTERMEDIATE: x15.sorted_init_ops - -include Makefrag.am - -x15.sorted_init_ops: $(filter %.c,$(x15_SOURCES)) - $(AM_V_GEN)$(top_srcdir)/tools/tsort_init_ops.sh "$(COMPILE)" "$@" $^ diff --git a/Makefrag.am b/Makefrag.am deleted file mode 100644 index 24d700a..0000000 --- a/Makefrag.am +++ /dev/null @@ -1,192 +0,0 @@ -include arch/x86/Makefrag.am -include doc/Makefrag.am - -EXTRA_DIST += \ - tools/qemu.sh \ - tools/tsort_init_ops.sh - -x15_SOURCES += \ - include/assert.h \ - include/limits.h \ - include/stdio.h \ - include/string.h - -x15_SOURCES += \ - kern/arg.c \ - kern/arg.h \ - kern/atomic.h \ - kern/bitmap.c \ - kern/bitmap.h \ - kern/bitmap_i.h \ - kern/cbuf.c \ - kern/cbuf.h \ - kern/clock.c \ - kern/clock.h \ - kern/clock_i.h \ - kern/condition.c \ - kern/condition.h \ - kern/condition_types.h \ - kern/console.c \ - kern/console.h \ - kern/config.h \ - kern/cpumap.c \ - kern/cpumap.h \ - kern/error.c \ - kern/error.h \ - kern/hash.h \ - kern/hlist.h \ - kern/hlist_types.h \ - kern/fmt.c \ - kern/fmt.h \ - kern/init.c \ - kern/init.h \ - kern/init_i.h \ - kern/intr.c \ - kern/intr.h \ - kern/kernel.c \ - kern/kernel.h \ - kern/kmem.c \ - kern/kmem.h \ - kern/kmem_i.h \ - kern/list.h \ - kern/list_types.h \ - kern/llsync.c \ - kern/llsync.h \ - kern/llsync_i.h \ - kern/log.c \ - kern/log.h \ - kern/log2.h \ - kern/macros.h \ - kern/mutex.c \ - kern/mutex.h \ - kern/mutex_types.h \ - kern/mutex/mutex_adaptive_i.h \ - kern/mutex/mutex_adaptive_types.h \ - kern/mutex/mutex_pi_i.h \ - kern/mutex/mutex_pi_types.h \ - kern/mutex/mutex_plain_i.h \ - kern/mutex/mutex_plain_types.h \ - kern/panic.c \ - kern/panic.h \ - kern/percpu.c \ - kern/percpu.h \ - kern/plist.c \ - kern/plist.h \ - kern/plist_types.h \ - kern/printf.c \ - kern/printf.h \ - kern/rbtree.c \ - kern/rbtree.h \ - kern/rbtree_i.h \ - kern/rdxtree.c \ - kern/rdxtree.h \ - kern/rdxtree_i.h \ - kern/rtmutex.c \ - kern/rtmutex.h \ - kern/rtmutex_i.h \ - kern/rtmutex_types.h \ - kern/semaphore.c \ - kern/semaphore.h \ - kern/semaphore_i.h \ - kern/shutdown.c \ - kern/shutdown.h \ - kern/sleepq.c \ - kern/sleepq.h \ - kern/slist.h \ - kern/slist_types.h \ - kern/spinlock.c \ - kern/spinlock.h \ - kern/spinlock_i.h \ - kern/spinlock_types.h \ - kern/sref.c \ - kern/sref.h \ - kern/sref_i.h \ - kern/string.c \ - kern/string.h \ - kern/syscnt.c \ - kern/syscnt.h \ - kern/syscnt_types.h \ - kern/task.c \ - kern/task.h \ - kern/thread.c \ - kern/thread.h \ - kern/thread_i.h \ - kern/timer.c \ - kern/timer.h \ - kern/timer_i.h \ - kern/turnstile.c \ - kern/turnstile.h \ - kern/turnstile_types.h \ - kern/types.h \ - kern/work.c \ - kern/work.h \ - kern/work_i.h \ - kern/xcall.c \ - kern/xcall.h - -if USE_MUTEX_ADAPTIVE - x15_SOURCES += kern/mutex/mutex_adaptive.c -else -if USE_MUTEX_PLAIN - x15_SOURCES += kern/mutex/mutex_plain.c -endif -endif - -if ENABLE_SHELL -x15_SOURCES += \ - kern/shell.c \ - kern/shell.h -endif ENABLE_SHELL - -x15_SOURCES += \ - vm/vm_adv.h \ - vm/vm_inherit.h \ - vm/vm_kmem.c \ - vm/vm_kmem.h \ - vm/vm_map.c \ - vm/vm_map.h \ - vm/vm_object.c \ - vm/vm_object.h \ - vm/vm_object_types.h \ - vm/vm_page.c \ - vm/vm_page.h \ - vm/vm_prot.h - -x15_SOURCES += \ - test/test.h - -if ENABLE_TEST_LLSYNC_DEFER -x15_SOURCES += test/test_llsync_defer.c -endif ENABLE_TEST_LLSYNC_DEFER - -if ENABLE_TEST_MUTEX -x15_SOURCES += test/test_mutex.c -endif ENABLE_TEST_MUTEX - -if ENABLE_TEST_MUTEX_PI -x15_SOURCES += test/test_mutex_pi.c -endif ENABLE_TEST_MUTEX_PI - -if ENABLE_TEST_PMAP_UPDATE_MP -x15_SOURCES += test/test_pmap_update_mp.c -endif ENABLE_TEST_PMAP_UPDATE_MP - -if ENABLE_TEST_SREF_DIRTY_ZEROES -x15_SOURCES += test/test_sref_dirty_zeroes.c -endif ENABLE_TEST_SREF_DIRTY_ZEROES - -if ENABLE_TEST_SREF_NOREF -x15_SOURCES += test/test_sref_noref.c -endif ENABLE_TEST_SREF_NOREF - -if ENABLE_TEST_SREF_WEAKREF -x15_SOURCES += test/test_sref_weakref.c -endif ENABLE_TEST_SREF_WEAKREF - -if ENABLE_TEST_VM_PAGE_FILL -x15_SOURCES += test/test_vm_page_fill.c -endif ENABLE_TEST_VM_PAGE_FILL - -if ENABLE_TEST_XCALL -x15_SOURCES += test/test_xcall.c -endif ENABLE_TEST_XCALL diff --git a/arch/x86/Makefrag.am b/arch/x86/Makefrag.am deleted file mode 100644 index 016e31c..0000000 --- a/arch/x86/Makefrag.am +++ /dev/null @@ -1,79 +0,0 @@ -EXTRA_DIST += arch/x86/x15.lds.S - -if X86 - -# Store unwind information in .debug_frame instead of .eh_frame. Unwind -# tables aren't used at runtime, so using a debug section reduces the kernel -# code size. -AM_CFLAGS += -fno-asynchronous-unwind-tables - -# For now, use frame pointers for convenient stack tracing. -AM_CFLAGS += -fno-omit-frame-pointer - -x15_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 - -if X86_64 - -AM_CPPFLAGS += -m64 -AM_CFLAGS += -mcmodel=kernel -mno-red-zone -x15_LDFLAGS += -m64 - -else !X86_64 - -AM_CPPFLAGS += -m32 -x15_LDFLAGS += -m32 - -endif !X86_64 - -x15_SOURCES += \ - arch/x86/machine/acpi.c \ - arch/x86/machine/acpi.h \ - arch/x86/machine/atcons.c \ - arch/x86/machine/atcons.h \ - arch/x86/machine/atkbd.c \ - arch/x86/machine/atkbd.h \ - arch/x86/machine/asm.h \ - arch/x86/machine/atomic.h \ - arch/x86/machine/biosmem.c \ - arch/x86/machine/biosmem.h \ - arch/x86/machine/boot_asm.S \ - arch/x86/machine/boot.c \ - arch/x86/machine/boot.h \ - arch/x86/machine/config.h \ - arch/x86/machine/cga.c \ - arch/x86/machine/cga.h \ - arch/x86/machine/cpu_asm.S \ - arch/x86/machine/cpu.c \ - arch/x86/machine/cpu.h \ - arch/x86/machine/elf.h \ - arch/x86/machine/io.h \ - arch/x86/machine/ioapic.c \ - arch/x86/machine/ioapic.h \ - arch/x86/machine/lapic.c \ - arch/x86/machine/lapic.h \ - arch/x86/machine/multiboot.h \ - arch/x86/machine/page.h \ - arch/x86/machine/pic.c \ - arch/x86/machine/pic.h \ - arch/x86/machine/pit.c \ - arch/x86/machine/pit.h \ - arch/x86/machine/pmap.c \ - arch/x86/machine/pmap.h \ - arch/x86/machine/pmem.h \ - arch/x86/machine/ssp.c \ - arch/x86/machine/ssp.h \ - arch/x86/machine/strace.c \ - arch/x86/machine/strace.h \ - arch/x86/machine/string.c \ - arch/x86/machine/string.h \ - arch/x86/machine/tcb_asm.S \ - arch/x86/machine/tcb.c \ - arch/x86/machine/tcb.h \ - arch/x86/machine/trap_asm.S \ - arch/x86/machine/trap.c \ - arch/x86/machine/trap.h \ - arch/x86/machine/types.h \ - arch/x86/machine/uart.c \ - arch/x86/machine/uart.h - -endif X86 diff --git a/arch/x86/configfrag.ac b/arch/x86/configfrag.ac deleted file mode 100644 index 0857368..0000000 --- a/arch/x86/configfrag.ac +++ /dev/null @@ -1,47 +0,0 @@ -m4_define([x86_ENABLE_PAE], - [AC_DEFINE([X15_X86_PAE], [1], [use PAE page translation])]) - -m4_define([x86_SELECT_I386], - [subarch=i386 - AS_IF([test x"$enable_x86_pae" = xyes], [x86_ENABLE_PAE])]) - -m4_define([x86_SELECT_AMD64], - [subarch=amd64 - AS_IF([test x"$enable_x86_pae" = xyes], - [AC_MSG_WARN([pae option available for 32-bits builds only, ignoring])])]) - -m4_define([x86_SELECT], - [arch=x86 - AC_ARG_ENABLE([x86-pae], - [AS_HELP_STRING([--enable-x86-pae], - [enable physical address extension (32-bits only)])], - [], - [enable_x86_pae=no]) - - AS_IF([test -z "$enable_64bits"], - [AS_CASE(["$host_cpu"], - [i?86], [enable_64bits=no], - [x86_64], [enable_64bits=yes])]) - - AS_IF([test x"$enable_64bits" = xyes], [x86_SELECT_AMD64], [x86_SELECT_I386]) - - # Never generate instructions that are unhandled in kernel mode - AX_APPEND_COMPILE_FLAGS([ \ - -mno-mmx \ - -mno-3dnow \ - -mno-sse \ - -mno-sse2 \ - -mno-avx]) - - AC_DEFINE_UNQUOTED([X15_X86_SUBARCH], [$subarch], [subarch]) - - AS_ECHO - AC_MSG_NOTICE([target: $arch ($subarch)]) - AS_IF([test x"$subarch" = xi386], - [AC_MSG_NOTICE([x86: physical address extension: $enable_x86_pae])])]) - -AS_CASE(["$host_cpu"], - [i?86|x86_64], [x86_SELECT]) - -AM_CONDITIONAL([X86], [test x"$arch" = xx86]) -AM_CONDITIONAL([X86_64], [test x"$arch" = xx86 -a x"$enable_64bits" = xyes]) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index c544a5d..0000000 --- a/configure.ac +++ /dev/null @@ -1,177 +0,0 @@ -AC_PREREQ([2.69]) -AC_INIT([X15], [0.1], [rbraun@sceen.net], [x15]) - -AC_CONFIG_SRCDIR([kern/config.h]) -AC_CONFIG_AUX_DIR([build-aux]) -AC_CONFIG_MACRO_DIR([tools/m4]) - -# Artificially create the doc subdirectory since Automake apparently -# won't do it itself. -AC_CONFIG_COMMANDS([doc/.empty]) - -AM_INIT_AUTOMAKE([foreign subdir-objects 1.13]) - -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], - [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])]) - -AC_CANONICAL_HOST -AC_PROG_CPP -AC_PROG_CC -AM_PROG_AS -AM_PROG_CC_C_O - -AX_PROG_ASCIIDOC([AC_MSG_NOTICE([asciidoc not found, not building html documentation])]) -AX_PROG_HIGHLIGHT([AS_IF([test x"$ASCIIDOC" != x], - [AC_MSG_NOTICE([highlight not found, not building html documentation])])]) -AX_PROG_A2X([AC_MSG_NOTICE([a2x not found, not building man pages])]) - -# Disable PIE if enabled by default -AX_APPEND_COMPILE_FLAGS([-no-pie -fno-pie]) - -AC_HEADER_ASSERT() - -# --enable options - -AC_ARG_ENABLE([64bits], - [AS_HELP_STRING([--enable-64bits], - [build for a 64-bits target processor])], - [], - [enable_64bits=default]) - -AC_ARG_ENABLE([shell], - [AS_HELP_STRING([--enable-shell], - [enable the diagnostics shell])], - [], - [enable_shell=no]) - -AC_ARG_ENABLE([test-module], - [AS_HELP_STRING([--enable-test-module=TEST_MODULE], - [run the given test module instead of booting])], - [enable_test_module=$enableval], - [enable_test_module=]) - -AC_ARG_ENABLE([thread-stack-guard], - [AS_HELP_STRING([--enable-thread-stack-guard], - [enable kernel thread stack guard pages])], - [], - [enable_thread_stack_guard=no]) - -# --with options - -AC_ARG_WITH([clock-freq], - [AS_HELP_STRING([--with-clock-freq=CLOCK_FREQ], - [set the low resolution clock frequency (100-1000 Hz)])], - [opt_clock_freq=$withval], - [opt_clock_freq=200]) - -AC_ARG_WITH([max-cpus], - [AS_HELP_STRING([--with-max-cpus=MAX_CPUS], - [set the maximum number of supported processors])], - [opt_max_cpus=$withval], - [opt_max_cpus=128]) - -AC_ARG_WITH([mutex-impl], - [AS_HELP_STRING([--with-mutex-impl=MUTEX_IMPL], - [select the mutex implementation (adaptive, pi, or plain)])], - [opt_mutex_impl=$withval], - [opt_mutex_impl=plain]) - -# 32/64-bits processor selection - -AS_IF([test -z "$enable_64bits"], [enable_64bits=yes], - [test x"$enable_64bits" = x"default"], [enable_64bits=], - [test x"$enable_64bits" != xyes -a x"$enable_64bits" != xno], - [AC_MSG_ERROR([target processor must be either 32-bits or 64-bits])]) - -# Architecture-specific configuration - -m4_include([arch/x86/configfrag.ac]) - -AS_IF([test x"$arch" = x], [AC_MSG_ERROR([unsupported architecture])]) -AC_SUBST([arch]) - -# Shell selection - -AS_IF([test x"$enable_shell" = xyes], - [AC_DEFINE_UNQUOTED([X15_ENABLE_SHELL], [], - [Enable the diagnostics shell])]) -AM_CONDITIONAL([ENABLE_SHELL], - [test x"$enable_shell" = xyes]) -AC_MSG_NOTICE([diagnostics shell: $enable_shell]) - -# Test module selection - -m4_define([DEFINE_TEST_MODULE], - [AM_CONDITIONAL([$1], [test x"$enable_test_module" = x"$2"]) - AM_COND_IF([$1], [AC_DEFINE_UNQUOTED([X15_RUN_TEST_MODULE], [], - [run test module instead of booting])])]) - -DEFINE_TEST_MODULE(ENABLE_TEST_LLSYNC_DEFER, llsync_defer) -DEFINE_TEST_MODULE(ENABLE_TEST_MUTEX, mutex) -DEFINE_TEST_MODULE(ENABLE_TEST_MUTEX_PI, mutex_pi) -DEFINE_TEST_MODULE(ENABLE_TEST_PMAP_UPDATE_MP, pmap_update_mp) -DEFINE_TEST_MODULE(ENABLE_TEST_SREF_DIRTY_ZEROES, sref_dirty_zeroes) -DEFINE_TEST_MODULE(ENABLE_TEST_SREF_NOREF, sref_noref) -DEFINE_TEST_MODULE(ENABLE_TEST_SREF_WEAKREF, sref_weakref) -DEFINE_TEST_MODULE(ENABLE_TEST_VM_PAGE_FILL, vm_page_fill) -DEFINE_TEST_MODULE(ENABLE_TEST_XCALL, xcall) - -AS_IF([test "$enable_test_module" -a -f test/test_$enable_test_module.c], - [AC_MSG_NOTICE([test module enabled: $enable_test_module])], - [test "$enable_test_module"], - [AC_MSG_ERROR([invalid test module])]) - -# Thread stack guard selection - -AS_IF([test x"$enable_thread_stack_guard" = xyes], - [AC_DEFINE_UNQUOTED([X15_ENABLE_THREAD_STACK_GUARD], [], - [Enable the use of guard pages for thread stacks])]) -AC_MSG_NOTICE([thread stack guard pages: $enable_thread_stack_guard]) - -# Low resolution clock frequency - -AC_DEFINE_UNQUOTED([X15_CLOCK_FREQ], [$opt_clock_freq], - [low resolution clock frequency]) -AC_MSG_NOTICE([low resolution clock frequency: $opt_clock_freq Hz]) - -# Maximum number of supported processors - -AC_DEFINE_UNQUOTED([X15_MAX_CPUS], [$opt_max_cpus], - [maximum number of supported processors]) -AC_MSG_NOTICE([maximum number of supported processors: $opt_max_cpus]) - -# Mutex implementation selection - -m4_define([SELECT_MUTEX_IMPL], - [mutex_impl_desc="$2" - AC_DEFINE_UNQUOTED([$1], [], [$3])]) - -AS_IF([test x"$opt_mutex_impl" = xadaptive], - [SELECT_MUTEX_IMPL([X15_USE_MUTEX_ADAPTIVE], - [adaptive spinning], - [Use adaptive spinning mutex implementation])], - [test x"$opt_mutex_impl" = xpi], - [SELECT_MUTEX_IMPL([X15_USE_MUTEX_PI], - [priority inheritance], - [Use priority inheritance mutex implementation])], - [test x"$opt_mutex_impl" = xplain], - [SELECT_MUTEX_IMPL([X15_USE_MUTEX_PLAIN], - [plain], - [Use plain mutex implementation])], - [AC_MSG_ERROR([invalid mutex implementation])]) -AM_CONDITIONAL([USE_MUTEX_ADAPTIVE], - [test x"$opt_mutex_impl" = xadaptive]) -AM_CONDITIONAL([USE_MUTEX_PI], - [test x"$opt_mutex_impl" = xpi]) -AM_CONDITIONAL([USE_MUTEX_PLAIN], - [test x"$opt_mutex_impl" = xplain]) -AC_MSG_NOTICE([mutex implementation: $mutex_impl_desc]) - -# Output generation - -AS_ECHO - -AH_BOTTOM([#include ]) -AC_CONFIG_HEADER([config.h]) -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/doc/Makefrag.am b/doc/Makefrag.am deleted file mode 100644 index 4f9083d..0000000 --- a/doc/Makefrag.am +++ /dev/null @@ -1,33 +0,0 @@ -EXTRA_DIST += doc/asciidoc.conf - -x15_DOCS = \ - doc/cenv.9.txt \ - doc/init.9.txt \ - doc/intro.9.txt \ - doc/style.9.txt - -x15_SOURCES += $(x15_DOCS) - -if HAVE_ASCIIDOC -if HAVE_HIGHLIGHT - -html_DATA = $(x15_DOCS:%.9.txt=%.9.html) -MOSTLYCLEANFILES += $(html_DATA) - -$(html_DATA): doc/asciidoc.conf -html: $(html_DATA) -.PHONY: html - -endif HAVE_HIGHLIGHT -endif HAVE_ASCIIDOC - -if HAVE_A2X - -man9_MANS = $(x15_DOCS:%.9.txt=%.9) -MOSTLYCLEANFILES += $(man9_MANS) - -$(man9_MANS): doc/asciidoc.conf -man: $(man9_MANS) -.PHONY: man - -endif HAVE_A2X diff --git a/tools/m4/ax_append_compile_flags.m4 b/tools/m4/ax_append_compile_flags.m4 deleted file mode 100644 index 5b6f1af..0000000 --- a/tools/m4/ax_append_compile_flags.m4 +++ /dev/null @@ -1,67 +0,0 @@ -# ============================================================================ -# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html -# ============================================================================ -# -# SYNOPSIS -# -# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) -# -# DESCRIPTION -# -# For every FLAG1, FLAG2 it is checked whether the compiler works with the -# flag. If it does, the flag is added FLAGS-VARIABLE -# -# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. -# CFLAGS) is used. During the check the flag is always added to the -# current language's flags. -# -# If EXTRA-FLAGS is defined, it is added to the current language's default -# flags (e.g. CFLAGS) when the check is done. The check is thus made with -# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to -# force the compiler to issue an error when a bad flag is given. -# -# INPUT gives an alternative input source to AC_COMPILE_IFELSE. -# -# NOTE: This macro depends on the AX_APPEND_FLAG and -# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with -# AX_APPEND_LINK_FLAGS. -# -# LICENSE -# -# Copyright (c) 2011 Maarten Bosmans -# -# 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 6 - -AC_DEFUN([AX_APPEND_COMPILE_FLAGS], -[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) -AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) -for flag in $1; do - AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) -done -])dnl AX_APPEND_COMPILE_FLAGS diff --git a/tools/m4/ax_append_flag.m4 b/tools/m4/ax_append_flag.m4 deleted file mode 100644 index e8c5312..0000000 --- a/tools/m4/ax_append_flag.m4 +++ /dev/null @@ -1,71 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) -# -# DESCRIPTION -# -# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space -# added in between. -# -# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. -# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains -# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly -# FLAG. -# -# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim -# Copyright (c) 2011 Maarten Bosmans -# -# 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 7 - -AC_DEFUN([AX_APPEND_FLAG], -[dnl -AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF -AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) -AS_VAR_SET_IF(FLAGS,[ - AS_CASE([" AS_VAR_GET(FLAGS) "], - [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], - [ - AS_VAR_APPEND(FLAGS,[" $1"]) - AC_RUN_LOG([: FLAGS="$FLAGS"]) - ]) - ], - [ - AS_VAR_SET(FLAGS,[$1]) - AC_RUN_LOG([: FLAGS="$FLAGS"]) - ]) -AS_VAR_POPDEF([FLAGS])dnl -])dnl AX_APPEND_FLAG diff --git a/tools/m4/ax_check_compile_flag.m4 b/tools/m4/ax_check_compile_flag.m4 deleted file mode 100644 index dcabb92..0000000 --- a/tools/m4/ax_check_compile_flag.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) -# -# DESCRIPTION -# -# Check whether the given FLAG works with the current language's compiler -# or gives an error. (Warnings, however, are ignored) -# -# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on -# success/failure. -# -# If EXTRA-FLAGS is defined, it is added to the current language's default -# flags (e.g. CFLAGS) when the check is done. The check is thus made with -# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to -# force the compiler to issue an error when a bad flag is given. -# -# INPUT gives an alternative input source to AC_COMPILE_IFELSE. -# -# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this -# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. -# -# LICENSE -# -# Copyright (c) 2008 Guido U. Draheim -# Copyright (c) 2011 Maarten Bosmans -# -# 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 5 - -AC_DEFUN([AX_CHECK_COMPILE_FLAG], -[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF -AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl -AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ - ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS - _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" - AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], - [AS_VAR_SET(CACHEVAR,[yes])], - [AS_VAR_SET(CACHEVAR,[no])]) - _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) -AS_VAR_IF(CACHEVAR,yes, - [m4_default([$2], :)], - [m4_default([$3], :)]) -AS_VAR_POPDEF([CACHEVAR])dnl -])dnl AX_CHECK_COMPILE_FLAGS diff --git a/tools/m4/ax_prog_a2x.m4 b/tools/m4/ax_prog_a2x.m4 deleted file mode 100644 index 54a2f42..0000000 --- a/tools/m4/ax_prog_a2x.m4 +++ /dev/null @@ -1,46 +0,0 @@ -# SYNOPSIS -# -# AX_PROG_A2X(ACTION-IF-NOT-FOUND) -# -# DESCRIPTION -# -# Check whether the a2x command is available on system, and provide -# the HAVE_A2X Automake conditional. -# -# ACTION-IF-NOT-FOUND is shell commands to execute on failure. -# -# LICENSE -# -# Copyright (c) 2017 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AC_DEFUN([AX_PROG_A2X], - [AC_ARG_VAR([A2X], [path to a2x command]) - AC_CHECK_PROG([A2X], [a2x], [a2x]) - AS_IF([test x"$A2X" = x], [$1]) - AM_CONDITIONAL([HAVE_A2X], [test x"$A2X" != x])]) diff --git a/tools/m4/ax_prog_asciidoc.m4 b/tools/m4/ax_prog_asciidoc.m4 deleted file mode 100644 index 6ca3421..0000000 --- a/tools/m4/ax_prog_asciidoc.m4 +++ /dev/null @@ -1,46 +0,0 @@ -# SYNOPSIS -# -# AX_PROG_ASCIIDOC(ACTION-IF-NOT-FOUND) -# -# DESCRIPTION -# -# Check whether the asciidoc command is available on system, and provide -# the HAVE_ASCIIDOC Automake conditional. -# -# ACTION-IF-NOT-FOUND is shell commands to execute on failure. -# -# LICENSE -# -# Copyright (c) 2017 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AC_DEFUN([AX_PROG_ASCIIDOC], - [AC_ARG_VAR([ASCIIDOC], [path to asciidoc command]) - AC_CHECK_PROG([ASCIIDOC], [asciidoc], [asciidoc]) - AS_IF([test x"$ASCIIDOC" = x], [$1]) - AM_CONDITIONAL([HAVE_ASCIIDOC], [test x"$ASCIIDOC" != x])]) diff --git a/tools/m4/ax_prog_highlight.m4 b/tools/m4/ax_prog_highlight.m4 deleted file mode 100644 index 4b49738..0000000 --- a/tools/m4/ax_prog_highlight.m4 +++ /dev/null @@ -1,46 +0,0 @@ -# SYNOPSIS -# -# AX_PROG_HIGHLIGHT(ACTION-IF-NOT-FOUND) -# -# DESCRIPTION -# -# Check whether the highlight command is available on system, and provide -# the HAVE_HIGHLIGHT Automake conditional. -# -# ACTION-IF-NOT-FOUND is shell commands to execute on failure. -# -# LICENSE -# -# Copyright (c) 2017 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 3 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 . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -AC_DEFUN([AX_PROG_HIGHLIGHT], - [AC_ARG_VAR([HIGHLIGHT], [path to highlight command]) - AC_CHECK_PROG([HIGHLIGHT], [highlight], [highlight]) - AS_IF([test x"$HIGHLIGHT" = x], [$1]) - AM_CONDITIONAL([HAVE_HIGHLIGHT], [test x"$HIGHLIGHT" != x])]) diff --git a/tools/m4/ax_require_defined.m4 b/tools/m4/ax_require_defined.m4 deleted file mode 100644 index 17c3eab..0000000 --- a/tools/m4/ax_require_defined.m4 +++ /dev/null @@ -1,37 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_REQUIRE_DEFINED(MACRO) -# -# DESCRIPTION -# -# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have -# been defined and thus are available for use. This avoids random issues -# where a macro isn't expanded. Instead the configure script emits a -# non-fatal: -# -# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found -# -# It's like AC_REQUIRE except it doesn't expand the required macro. -# -# Here's an example: -# -# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) -# -# LICENSE -# -# Copyright (c) 2014 Mike Frysinger -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 2 - -AC_DEFUN([AX_REQUIRE_DEFINED], [dnl - m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) -])dnl AX_REQUIRE_DEFINED -- cgit v1.2.3 From d115a8cee02be828d46651a5fc91fdbfe23985f2 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 21 Sep 2017 01:17:43 +0200 Subject: Import kconfig from Linux 4.13 --- tools/kconfig/.gitignore | 22 + tools/kconfig/Makefile | 296 ++++ tools/kconfig/POTFILES.in | 12 + tools/kconfig/check.sh | 13 + tools/kconfig/conf.c | 723 +++++++++ tools/kconfig/confdata.c | 1249 +++++++++++++++ tools/kconfig/expr.c | 1206 +++++++++++++++ tools/kconfig/expr.h | 240 +++ tools/kconfig/gconf.c | 1521 ++++++++++++++++++ tools/kconfig/gconf.glade | 661 ++++++++ tools/kconfig/images.c | 326 ++++ tools/kconfig/kxgettext.c | 235 +++ tools/kconfig/list.h | 131 ++ tools/kconfig/lkc.h | 186 +++ tools/kconfig/lkc_proto.h | 52 + tools/kconfig/lxdialog/.gitignore | 4 + tools/kconfig/lxdialog/BIG.FAT.WARNING | 4 + tools/kconfig/lxdialog/check-lxdialog.sh | 91 ++ tools/kconfig/lxdialog/checklist.c | 332 ++++ tools/kconfig/lxdialog/dialog.h | 257 ++++ tools/kconfig/lxdialog/inputbox.c | 301 ++++ tools/kconfig/lxdialog/menubox.c | 437 ++++++ tools/kconfig/lxdialog/textbox.c | 408 +++++ tools/kconfig/lxdialog/util.c | 713 +++++++++ tools/kconfig/lxdialog/yesno.c | 114 ++ tools/kconfig/mconf.c | 1047 +++++++++++++ tools/kconfig/menu.c | 720 +++++++++ tools/kconfig/merge_config.sh | 170 ++ tools/kconfig/nconf.c | 1563 +++++++++++++++++++ tools/kconfig/nconf.gui.c | 663 ++++++++ tools/kconfig/nconf.h | 96 ++ tools/kconfig/qconf.cc | 1879 +++++++++++++++++++++++ tools/kconfig/qconf.h | 330 ++++ tools/kconfig/streamline_config.pl | 682 ++++++++ tools/kconfig/symbol.c | 1414 +++++++++++++++++ tools/kconfig/util.c | 147 ++ tools/kconfig/zconf.gperf | 50 + tools/kconfig/zconf.hash.c_shipped | 297 ++++ tools/kconfig/zconf.l | 374 +++++ tools/kconfig/zconf.lex.c_shipped | 2473 ++++++++++++++++++++++++++++++ tools/kconfig/zconf.tab.c_shipped | 2471 +++++++++++++++++++++++++++++ tools/kconfig/zconf.y | 754 +++++++++ 42 files changed, 24664 insertions(+) create mode 100644 tools/kconfig/.gitignore create mode 100644 tools/kconfig/Makefile create mode 100644 tools/kconfig/POTFILES.in create mode 100755 tools/kconfig/check.sh create mode 100644 tools/kconfig/conf.c create mode 100644 tools/kconfig/confdata.c create mode 100644 tools/kconfig/expr.c create mode 100644 tools/kconfig/expr.h create mode 100644 tools/kconfig/gconf.c create mode 100644 tools/kconfig/gconf.glade create mode 100644 tools/kconfig/images.c create mode 100644 tools/kconfig/kxgettext.c create mode 100644 tools/kconfig/list.h create mode 100644 tools/kconfig/lkc.h create mode 100644 tools/kconfig/lkc_proto.h create mode 100644 tools/kconfig/lxdialog/.gitignore create mode 100644 tools/kconfig/lxdialog/BIG.FAT.WARNING create mode 100755 tools/kconfig/lxdialog/check-lxdialog.sh create mode 100644 tools/kconfig/lxdialog/checklist.c create mode 100644 tools/kconfig/lxdialog/dialog.h create mode 100644 tools/kconfig/lxdialog/inputbox.c create mode 100644 tools/kconfig/lxdialog/menubox.c create mode 100644 tools/kconfig/lxdialog/textbox.c create mode 100644 tools/kconfig/lxdialog/util.c create mode 100644 tools/kconfig/lxdialog/yesno.c create mode 100644 tools/kconfig/mconf.c create mode 100644 tools/kconfig/menu.c create mode 100755 tools/kconfig/merge_config.sh create mode 100644 tools/kconfig/nconf.c create mode 100644 tools/kconfig/nconf.gui.c create mode 100644 tools/kconfig/nconf.h create mode 100644 tools/kconfig/qconf.cc create mode 100644 tools/kconfig/qconf.h create mode 100755 tools/kconfig/streamline_config.pl create mode 100644 tools/kconfig/symbol.c create mode 100644 tools/kconfig/util.c create mode 100644 tools/kconfig/zconf.gperf create mode 100644 tools/kconfig/zconf.hash.c_shipped create mode 100644 tools/kconfig/zconf.l create mode 100644 tools/kconfig/zconf.lex.c_shipped create mode 100644 tools/kconfig/zconf.tab.c_shipped create mode 100644 tools/kconfig/zconf.y diff --git a/tools/kconfig/.gitignore b/tools/kconfig/.gitignore new file mode 100644 index 0000000..be603c4 --- /dev/null +++ b/tools/kconfig/.gitignore @@ -0,0 +1,22 @@ +# +# Generated files +# +config* +*.lex.c +*.tab.c +*.tab.h +zconf.hash.c +*.moc +gconf.glade.h +*.pot +*.mo + +# +# configuration programs +# +conf +mconf +nconf +qconf +gconf +kxgettext diff --git a/tools/kconfig/Makefile b/tools/kconfig/Makefile new file mode 100644 index 0000000..eb81446 --- /dev/null +++ b/tools/kconfig/Makefile @@ -0,0 +1,296 @@ +# =========================================================================== +# Kernel configuration targets +# These targets are used from top-level makefile + +PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \ + localmodconfig localyesconfig + +ifdef KBUILD_KCONFIG +Kconfig := $(KBUILD_KCONFIG) +else +Kconfig := Kconfig +endif + +ifeq ($(quiet),silent_) +silent := -s +endif + +# We need this, in case the user has it in its environment +unexport CONFIG_ + +xconfig: $(obj)/qconf + $< $(silent) $(Kconfig) + +gconfig: $(obj)/gconf + $< $(silent) $(Kconfig) + +menuconfig: $(obj)/mconf + $< $(silent) $(Kconfig) + +config: $(obj)/conf + $< $(silent) --oldaskconfig $(Kconfig) + +nconfig: $(obj)/nconf + $< $(silent) $(Kconfig) + +silentoldconfig: $(obj)/conf + $(Q)mkdir -p include/config include/generated + $(Q)test -e include/generated/autoksyms.h || \ + touch include/generated/autoksyms.h + $< $(silent) --$@ $(Kconfig) + +localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf + $(Q)mkdir -p include/config include/generated + $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config + $(Q)if [ -f .config ]; then \ + cmp -s .tmp.config .config || \ + (mv -f .config .config.old.1; \ + mv -f .tmp.config .config; \ + $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ + mv -f .config.old.1 .config.old) \ + else \ + mv -f .tmp.config .config; \ + $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ + fi + $(Q)rm -f .tmp.config + +# Create new linux.pot file +# Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files +update-po-config: $(obj)/kxgettext $(obj)/gconf.glade.h + $(Q)$(kecho) " GEN config.pot" + $(Q)xgettext --default-domain=linux \ + --add-comments --keyword=_ --keyword=N_ \ + --from-code=UTF-8 \ + --files-from=$(srctree)/scripts/kconfig/POTFILES.in \ + --directory=$(srctree) --directory=$(objtree) \ + --output $(obj)/config.pot + $(Q)sed -i s/CHARSET/UTF-8/ $(obj)/config.pot + $(Q)(for i in `ls $(srctree)/arch/*/Kconfig \ + $(srctree)/arch/*/um/Kconfig`; \ + do \ + $(kecho) " GEN $$i"; \ + $(obj)/kxgettext $$i \ + >> $(obj)/config.pot; \ + done ) + $(Q)$(kecho) " GEN linux.pot" + $(Q)msguniq --sort-by-file --to-code=UTF-8 $(obj)/config.pot \ + --output $(obj)/linux.pot + $(Q)rm -f $(obj)/config.pot + +# These targets map 1:1 to the commandline options of 'conf' +simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \ + alldefconfig randconfig listnewconfig olddefconfig +PHONY += $(simple-targets) + +$(simple-targets): $(obj)/conf + $< $(silent) --$@ $(Kconfig) + +PHONY += oldnoconfig savedefconfig defconfig + +# oldnoconfig is an alias of olddefconfig, because people already are dependent +# on its behavior (sets new symbols to their default value but not 'n') with the +# counter-intuitive name. +oldnoconfig: olddefconfig + +savedefconfig: $(obj)/conf + $< $(silent) --$@=defconfig $(Kconfig) + +defconfig: $(obj)/conf +ifeq ($(KBUILD_DEFCONFIG),) + $< $(silent) --defconfig $(Kconfig) +else +ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),) + @$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) +else + @$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'" + $(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG) +endif +endif + +%_defconfig: $(obj)/conf + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + +configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) + +%.config: $(obj)/conf + $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) + +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig + +PHONY += kvmconfig +kvmconfig: kvm_guest.config + @: + +PHONY += xenconfig +xenconfig: xen.config + @: + +PHONY += tinyconfig +tinyconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config + +# Help text used by make help +help: + @echo ' config - Update current config utilising a line-oriented program' + @echo ' nconfig - Update current config utilising a ncurses menu based' + @echo ' program' + @echo ' menuconfig - Update current config utilising a menu based program' + @echo ' xconfig - Update current config utilising a Qt based front-end' + @echo ' gconfig - Update current config utilising a GTK+ based front-end' + @echo ' oldconfig - Update current config utilising a provided .config as base' + @echo ' localmodconfig - Update current config disabling modules not loaded' + @echo ' localyesconfig - Update current config converting local mods to core' + @echo ' silentoldconfig - Same as oldconfig, but quietly, additionally update deps' + @echo ' defconfig - New config with default from ARCH supplied defconfig' + @echo ' savedefconfig - Save current config as ./defconfig (minimal config)' + @echo ' allnoconfig - New config where all options are answered with no' + @echo ' allyesconfig - New config where all options are accepted with yes' + @echo ' allmodconfig - New config selecting modules when possible' + @echo ' alldefconfig - New config with all symbols set to default' + @echo ' randconfig - New config with random answer to all options' + @echo ' listnewconfig - List new options' + @echo ' olddefconfig - Same as silentoldconfig but sets new symbols to their' + @echo ' default value' + @echo ' kvmconfig - Enable additional options for kvm guest kernel support' + @echo ' xenconfig - Enable additional options for xen dom0 and guest kernel support' + @echo ' tinyconfig - Configure the tiniest possible kernel' + +# lxdialog stuff +check-lxdialog := $(srctree)/$(src)/lxdialog/check-lxdialog.sh + +# Use recursively expanded variables so we do not call gcc unless +# we really need to do so. (Do not call gcc as part of make mrproper) +HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags) \ + -DLOCALE + +# =========================================================================== +# Shared Makefile for the various kconfig executables: +# conf: Used for defconfig, oldconfig and related targets +# nconf: Used for the nconfig target. +# Utilizes ncurses +# mconf: Used for the menuconfig target +# Utilizes the lxdialog package +# qconf: Used for the xconfig target +# Based on Qt which needs to be installed to compile it +# gconf: Used for the gconfig target +# Based on GTK+ which needs to be installed to compile it +# object files used by all kconfig flavours + +lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o +lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o + +conf-objs := conf.o zconf.tab.o +mconf-objs := mconf.o zconf.tab.o $(lxdialog) +nconf-objs := nconf.o zconf.tab.o nconf.gui.o +kxgettext-objs := kxgettext.o zconf.tab.o +qconf-cxxobjs := qconf.o +qconf-objs := zconf.tab.o +gconf-objs := gconf.o zconf.tab.o + +hostprogs-y := conf nconf mconf kxgettext qconf gconf + +clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck +clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h +clean-files += config.pot linux.pot + +# Check that we have the required ncurses stuff installed for lxdialog (menuconfig) +PHONY += $(obj)/dochecklxdialog +$(addprefix $(obj)/, mconf.o $(lxdialog)): $(obj)/dochecklxdialog +$(obj)/dochecklxdialog: + $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOSTLOADLIBES_mconf) + +always := dochecklxdialog + +# Add environment specific flags +HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(srctree)/$(src)/check.sh $(HOSTCC) $(HOSTCFLAGS)) + +# generated files seem to need this to find local include files +HOSTCFLAGS_zconf.lex.o := -I$(src) +HOSTCFLAGS_zconf.tab.o := -I$(src) + +LEX_PREFIX_zconf := zconf +YACC_PREFIX_zconf := zconf + +HOSTLOADLIBES_qconf = $(KC_QT_LIBS) +HOSTCXXFLAGS_qconf.o = $(KC_QT_CFLAGS) + +HOSTLOADLIBES_gconf = `pkg-config --libs gtk+-2.0 gmodule-2.0 libglade-2.0` +HOSTCFLAGS_gconf.o = `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \ + -Wno-missing-prototypes + +HOSTLOADLIBES_mconf = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC)) + +HOSTLOADLIBES_nconf = $(shell \ + pkg-config --libs menuw panelw ncursesw 2>/dev/null \ + || pkg-config --libs menu panel ncurses 2>/dev/null \ + || echo "-lmenu -lpanel -lncurses" ) +$(obj)/qconf.o: $(obj)/.tmp_qtcheck + +ifeq ($(MAKECMDGOALS),xconfig) +$(obj)/.tmp_qtcheck: $(src)/Makefile +-include $(obj)/.tmp_qtcheck + +# Qt needs some extra effort... +$(obj)/.tmp_qtcheck: + @set -e; $(kecho) " CHECK qt"; \ + if pkg-config --exists Qt5Core; then \ + cflags="-std=c++11 -fPIC `pkg-config --cflags Qt5Core Qt5Gui Qt5Widgets`"; \ + libs=`pkg-config --libs Qt5Core Qt5Gui Qt5Widgets`; \ + moc=`pkg-config --variable=host_bins Qt5Core`/moc; \ + elif pkg-config --exists QtCore; then \ + cflags=`pkg-config --cflags QtCore QtGui`; \ + libs=`pkg-config --libs QtCore QtGui`; \ + moc=`pkg-config --variable=moc_location QtCore`; \ + else \ + echo >&2 "*"; \ + echo >&2 "* Could not find Qt via pkg-config."; \ + echo >&2 "* Please install either Qt 4.8 or 5.x. and make sure it's in PKG_CONFIG_PATH"; \ + echo >&2 "*"; \ + exit 1; \ + fi; \ + echo "KC_QT_CFLAGS=$$cflags" > $@; \ + echo "KC_QT_LIBS=$$libs" >> $@; \ + echo "KC_QT_MOC=$$moc" >> $@ +endif + +$(obj)/gconf.o: $(obj)/.tmp_gtkcheck + +ifeq ($(MAKECMDGOALS),gconfig) +-include $(obj)/.tmp_gtkcheck + +# GTK+ needs some extra effort, too... +$(obj)/.tmp_gtkcheck: + @if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then \ + if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then \ + touch $@; \ + else \ + echo >&2 "*"; \ + echo >&2 "* GTK+ is present but version >= 2.0.0 is required."; \ + echo >&2 "*"; \ + false; \ + fi \ + else \ + echo >&2 "*"; \ + echo >&2 "* Unable to find the GTK+ installation. Please make sure that"; \ + echo >&2 "* the GTK+ 2.0 development package is correctly installed..."; \ + echo >&2 "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; \ + echo >&2 "*"; \ + false; \ + fi +endif + +$(obj)/zconf.tab.o: $(obj)/zconf.lex.c $(obj)/zconf.hash.c + +$(obj)/qconf.o: $(obj)/qconf.moc + +quiet_cmd_moc = MOC $@ + cmd_moc = $(KC_QT_MOC) -i $< -o $@ + +$(obj)/%.moc: $(src)/%.h $(obj)/.tmp_qtcheck + $(call cmd,moc) + +# Extract gconf menu items for i18n support +$(obj)/gconf.glade.h: $(obj)/gconf.glade + $(Q)intltool-extract --type=gettext/glade --srcdir=$(srctree) \ + $(obj)/gconf.glade diff --git a/tools/kconfig/POTFILES.in b/tools/kconfig/POTFILES.in new file mode 100644 index 0000000..9674573 --- /dev/null +++ b/tools/kconfig/POTFILES.in @@ -0,0 +1,12 @@ +scripts/kconfig/lxdialog/checklist.c +scripts/kconfig/lxdialog/inputbox.c +scripts/kconfig/lxdialog/menubox.c +scripts/kconfig/lxdialog/textbox.c +scripts/kconfig/lxdialog/util.c +scripts/kconfig/lxdialog/yesno.c +scripts/kconfig/mconf.c +scripts/kconfig/conf.c +scripts/kconfig/confdata.c +scripts/kconfig/gconf.c +scripts/kconfig/gconf.glade.h +scripts/kconfig/qconf.cc diff --git a/tools/kconfig/check.sh b/tools/kconfig/check.sh new file mode 100755 index 0000000..55b79ba --- /dev/null +++ b/tools/kconfig/check.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# Needed for systems without gettext +$* -x c -o /dev/null - > /dev/null 2>&1 << EOF +#include +int main() +{ + gettext(""); + return 0; +} +EOF +if [ ! "$?" -eq "0" ]; then + echo -DKBUILD_NO_NLS; +fi diff --git a/tools/kconfig/conf.c b/tools/kconfig/conf.c new file mode 100644 index 0000000..866369f --- /dev/null +++ b/tools/kconfig/conf.c @@ -0,0 +1,723 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lkc.h" + +static void conf(struct menu *menu); +static void check_conf(struct menu *menu); +static void xfgets(char *str, int size, FILE *in); + +enum input_mode { + oldaskconfig, + silentoldconfig, + oldconfig, + allnoconfig, + allyesconfig, + allmodconfig, + alldefconfig, + randconfig, + defconfig, + savedefconfig, + listnewconfig, + olddefconfig, +} input_mode = oldaskconfig; + +static int indent = 1; +static int tty_stdio; +static int valid_stdin = 1; +static int sync_kconfig; +static int conf_cnt; +static char line[PATH_MAX]; +static struct menu *rootEntry; + +static void print_help(struct menu *menu) +{ + struct gstr help = str_new(); + + menu_get_ext_help(menu, &help); + + printf("\n%s\n", str_get(&help)); + str_free(&help); +} + +static void strip(char *str) +{ + char *p = str; + int l; + + while ((isspace(*p))) + p++; + l = strlen(p); + if (p != str) + memmove(str, p, l + 1); + if (!l) + return; + p = str + l - 1; + while ((isspace(*p))) + *p-- = 0; +} + +static void check_stdin(void) +{ + if (!valid_stdin) { + printf(_("aborted!\n\n")); + printf(_("Console input/output is redirected. ")); + printf(_("Run 'make oldconfig' to update configuration.\n\n")); + exit(1); + } +} + +static int conf_askvalue(struct symbol *sym, const char *def) +{ + enum symbol_type type = sym_get_type(sym); + + if (!sym_has_value(sym)) + printf(_("(NEW) ")); + + line[0] = '\n'; + line[1] = 0; + + if (!sym_is_changable(sym)) { + printf("%s\n", def); + line[0] = '\n'; + line[1] = 0; + return 0; + } + + switch (input_mode) { + case oldconfig: + case silentoldconfig: + if (sym_has_value(sym)) { + printf("%s\n", def); + return 0; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, sizeof(line), stdin); + if (!tty_stdio) + printf("\n"); + return 1; + default: + break; + } + + switch (type) { + case S_INT: + case S_HEX: + case S_STRING: + printf("%s\n", def); + return 1; + default: + ; + } + printf("%s", line); + return 1; +} + +static int conf_string(struct menu *menu) +{ + struct symbol *sym = menu->sym; + const char *def; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + printf("(%s) ", sym->name); + def = sym_get_string_value(sym); + if (sym_get_string_value(sym)) + printf("[%s] ", def); + if (!conf_askvalue(sym, def)) + return 0; + switch (line[0]) { + case '\n': + break; + case '?': + /* print help */ + if (line[1] == '\n') { + print_help(menu); + def = NULL; + break; + } + /* fall through */ + default: + line[strlen(line)-1] = 0; + def = line; + } + if (def && sym_set_string_value(sym, def)) + return 0; + } +} + +static int conf_sym(struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate oldval, newval; + + while (1) { + printf("%*s%s ", indent - 1, "", _(menu->prompt->text)); + if (sym->name) + printf("(%s) ", sym->name); + putchar('['); + oldval = sym_get_tristate_value(sym); + switch (oldval) { + case no: + putchar('N'); + break; + case mod: + putchar('M'); + break; + case yes: + putchar('Y'); + break; + } + if (oldval != no && sym_tristate_within_range(sym, no)) + printf("/n"); + if (oldval != mod && sym_tristate_within_range(sym, mod)) + printf("/m"); + if (oldval != yes && sym_tristate_within_range(sym, yes)) + printf("/y"); + if (menu_has_help(menu)) + printf("/?"); + printf("] "); + if (!conf_askvalue(sym, sym_get_string_value(sym))) + return 0; + strip(line); + + switch (line[0]) { + case 'n': + case 'N': + newval = no; + if (!line[1] || !strcmp(&line[1], "o")) + break; + continue; + case 'm': + case 'M': + newval = mod; + if (!line[1]) + break; + continue; + case 'y': + case 'Y': + newval = yes; + if (!line[1] || !strcmp(&line[1], "es")) + break; + continue; + case 0: + newval = oldval; + break; + case '?': + goto help; + default: + continue; + } + if (sym_set_tristate_value(sym, newval)) + return 0; +help: + print_help(menu); + } +} + +static int conf_choice(struct menu *menu) +{ + struct symbol *sym, *def_sym; + struct menu *child; + bool is_new; + + sym = menu->sym; + is_new = !sym_has_value(sym); + if (sym_is_changable(sym)) { + conf_sym(menu); + sym_calc_value(sym); + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + return 0; + case yes: + break; + } + } else { + switch (sym_get_tristate_value(sym)) { + case no: + return 1; + case mod: + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + return 0; + case yes: + break; + } + } + + while (1) { + int cnt, def; + + printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu))); + def_sym = sym_get_choice_value(sym); + cnt = def = 0; + line[0] = 0; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (!child->sym) { + printf("%*c %s\n", indent, '*', _(menu_get_prompt(child))); + continue; + } + cnt++; + if (child->sym == def_sym) { + def = cnt; + printf("%*c", indent, '>'); + } else + printf("%*c", indent, ' '); + printf(" %d. %s", cnt, _(menu_get_prompt(child))); + if (child->sym->name) + printf(" (%s)", child->sym->name); + if (!sym_has_value(child->sym)) + printf(_(" (NEW)")); + printf("\n"); + } + printf(_("%*schoice"), indent - 1, ""); + if (cnt == 1) { + printf("[1]: 1\n"); + goto conf_childs; + } + printf("[1-%d", cnt); + if (menu_has_help(menu)) + printf("?"); + printf("]: "); + switch (input_mode) { + case oldconfig: + case silentoldconfig: + if (!is_new) { + cnt = def; + printf("%d\n", cnt); + break; + } + check_stdin(); + /* fall through */ + case oldaskconfig: + fflush(stdout); + xfgets(line, sizeof(line), stdin); + strip(line); + if (line[0] == '?') { + print_help(menu); + continue; + } + if (!line[0]) + cnt = def; + else if (isdigit(line[0])) + cnt = atoi(line); + else + continue; + break; + default: + break; + } + + conf_childs: + for (child = menu->list; child; child = child->next) { + if (!child->sym || !menu_is_visible(child)) + continue; + if (!--cnt) + break; + } + if (!child) + continue; + if (line[0] && line[strlen(line) - 1] == '?') { + print_help(child); + continue; + } + sym_set_choice_value(sym, child->sym); + for (child = child->list; child; child = child->next) { + indent += 2; + conf(child); + indent -= 2; + } + return 1; + } +} + +static void conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + prop = menu->prompt; + if (prop) { + const char *prompt; + + switch (prop->type) { + case P_MENU: + if ((input_mode == silentoldconfig || + input_mode == listnewconfig || + input_mode == olddefconfig) && + rootEntry != menu) { + check_conf(menu); + return; + } + /* fall through */ + case P_COMMENT: + prompt = menu_get_prompt(menu); + if (prompt) + printf("%*c\n%*c %s\n%*c\n", + indent, '*', + indent, '*', _(prompt), + indent, '*'); + default: + ; + } + } + + if (!sym) + goto conf_childs; + + if (sym_is_choice(sym)) { + conf_choice(menu); + if (sym->curr.tri != mod) + return; + goto conf_childs; + } + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + conf_string(menu); + break; + default: + conf_sym(menu); + break; + } + +conf_childs: + if (sym) + indent += 2; + for (child = menu->list; child; child = child->next) + conf(child); + if (sym) + indent -= 2; +} + +static void check_conf(struct menu *menu) +{ + struct symbol *sym; + struct menu *child; + + if (!menu_is_visible(menu)) + return; + + sym = menu->sym; + if (sym && !sym_has_value(sym)) { + if (sym_is_changable(sym) || + (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) { + if (input_mode == listnewconfig) { + if (sym->name && !sym_is_choice_value(sym)) { + printf("%s%s\n", CONFIG_, sym->name); + } + } else if (input_mode != olddefconfig) { + if (!conf_cnt++) + printf(_("*\n* Restart config...\n*\n")); + rootEntry = menu_get_parent_menu(menu); + conf(rootEntry); + } + } + } + + for (child = menu->list; child; child = child->next) + check_conf(child); +} + +static struct option long_opts[] = { + {"oldaskconfig", no_argument, NULL, oldaskconfig}, + {"oldconfig", no_argument, NULL, oldconfig}, + {"silentoldconfig", no_argument, NULL, silentoldconfig}, + {"defconfig", optional_argument, NULL, defconfig}, + {"savedefconfig", required_argument, NULL, savedefconfig}, + {"allnoconfig", no_argument, NULL, allnoconfig}, + {"allyesconfig", no_argument, NULL, allyesconfig}, + {"allmodconfig", no_argument, NULL, allmodconfig}, + {"alldefconfig", no_argument, NULL, alldefconfig}, + {"randconfig", no_argument, NULL, randconfig}, + {"listnewconfig", no_argument, NULL, listnewconfig}, + {"olddefconfig", no_argument, NULL, olddefconfig}, + /* + * oldnoconfig is an alias of olddefconfig, because people already + * are dependent on its behavior(sets new symbols to their default + * value but not 'n') with the counter-intuitive name. + */ + {"oldnoconfig", no_argument, NULL, olddefconfig}, + {NULL, 0, NULL, 0} +}; + +static void conf_usage(const char *progname) +{ + + printf("Usage: %s [-s] [option] \n", progname); + printf("[option] is _one_ of the following:\n"); + printf(" --listnewconfig List new options\n"); + printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); + printf(" --oldconfig Update a configuration using a provided .config as base\n"); + printf(" --silentoldconfig Same as oldconfig, but quietly, additionally update deps\n"); + printf(" --olddefconfig Same as silentoldconfig but sets new symbols to their default value\n"); + printf(" --oldnoconfig An alias of olddefconfig\n"); + printf(" --defconfig New config with default defined in \n"); + printf(" --savedefconfig Save the minimal current configuration to \n"); + printf(" --allnoconfig New config where all options are answered with no\n"); + printf(" --allyesconfig New config where all options are answered with yes\n"); + printf(" --allmodconfig New config where all options are answered with mod\n"); + printf(" --alldefconfig New config with all symbols set to default\n"); + printf(" --randconfig New config with random answer to all options\n"); +} + +int main(int ac, char **av) +{ + const char *progname = av[0]; + int opt; + const char *name, *defconfig_file = NULL /* gcc uninit */; + struct stat tmpstat; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + tty_stdio = isatty(0) && isatty(1) && isatty(2); + + while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) { + if (opt == 's') { + conf_set_message_callback(NULL); + continue; + } + input_mode = (enum input_mode)opt; + switch (opt) { + case silentoldconfig: + sync_kconfig = 1; + break; + case defconfig: + case savedefconfig: + defconfig_file = optarg; + break; + case randconfig: + { + struct timeval now; + unsigned int seed; + char *seed_env; + + /* + * Use microseconds derived seed, + * compensate for systems where it may be zero + */ + gettimeofday(&now, NULL); + seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1)); + + seed_env = getenv("KCONFIG_SEED"); + if( seed_env && *seed_env ) { + char *endp; + int tmp = (int)strtol(seed_env, &endp, 0); + if (*endp == '\0') { + seed = tmp; + } + } + fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed ); + srand(seed); + break; + } + case oldaskconfig: + case oldconfig: + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case listnewconfig: + case olddefconfig: + break; + case '?': + conf_usage(progname); + exit(1); + break; + } + } + if (ac == optind) { + printf(_("%s: Kconfig file missing\n"), av[0]); + conf_usage(progname); + exit(1); + } + name = av[optind]; + conf_parse(name); + //zconfdump(stdout); + if (sync_kconfig) { + name = conf_get_configname(); + if (stat(name, &tmpstat)) { + fprintf(stderr, _("***\n" + "*** Configuration file \"%s\" not found!\n" + "***\n" + "*** Please run some configurator (e.g. \"make oldconfig\" or\n" + "*** \"make menuconfig\" or \"make xconfig\").\n" + "***\n"), name); + exit(1); + } + } + + switch (input_mode) { + case defconfig: + if (!defconfig_file) + defconfig_file = conf_get_default_confname(); + if (conf_read(defconfig_file)) { + printf(_("***\n" + "*** Can't find default configuration \"%s\"!\n" + "***\n"), defconfig_file); + exit(1); + } + break; + case savedefconfig: + case silentoldconfig: + case oldaskconfig: + case oldconfig: + case listnewconfig: + case olddefconfig: + conf_read(NULL); + break; + case allnoconfig: + case allyesconfig: + case allmodconfig: + case alldefconfig: + case randconfig: + name = getenv("KCONFIG_ALLCONFIG"); + if (!name) + break; + if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) { + if (conf_read_simple(name, S_DEF_USER)) { + fprintf(stderr, + _("*** Can't read seed configuration \"%s\"!\n"), + name); + exit(1); + } + break; + } + switch (input_mode) { + case allnoconfig: name = "allno.config"; break; + case allyesconfig: name = "allyes.config"; break; + case allmodconfig: name = "allmod.config"; break; + case alldefconfig: name = "alldef.config"; break; + case randconfig: name = "allrandom.config"; break; + default: break; + } + if (conf_read_simple(name, S_DEF_USER) && + conf_read_simple("all.config", S_DEF_USER)) { + fprintf(stderr, + _("*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n"), + name); + exit(1); + } + break; + default: + break; + } + + if (sync_kconfig) { + if (conf_get_changed()) { + name = getenv("KCONFIG_NOSILENTUPDATE"); + if (name && *name) { + fprintf(stderr, + _("\n*** The configuration requires explicit update.\n\n")); + return 1; + } + } + valid_stdin = tty_stdio; + } + + switch (input_mode) { + case allnoconfig: + conf_set_all_new_symbols(def_no); + break; + case allyesconfig: + conf_set_all_new_symbols(def_yes); + break; + case allmodconfig: + conf_set_all_new_symbols(def_mod); + break; + case alldefconfig: + conf_set_all_new_symbols(def_default); + break; + case randconfig: + /* Really nothing to do in this loop */ + while (conf_set_all_new_symbols(def_random)) ; + break; + case defconfig: + conf_set_all_new_symbols(def_default); + break; + case savedefconfig: + break; + case oldaskconfig: + rootEntry = &rootmenu; + conf(&rootmenu); + input_mode = silentoldconfig; + /* fall through */ + case oldconfig: + case listnewconfig: + case olddefconfig: + case silentoldconfig: + /* Update until a loop caused no more changes */ + do { + conf_cnt = 0; + check_conf(&rootmenu); + } while (conf_cnt && + (input_mode != listnewconfig && + input_mode != olddefconfig)); + break; + } + + if (sync_kconfig) { + /* silentoldconfig is used during the build so we shall update autoconf. + * All other commands are only used to generate a config. + */ + if (conf_get_changed() && conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + exit(1); + } + if (conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during update of the configuration.\n\n")); + return 1; + } + } else if (input_mode == savedefconfig) { + if (conf_write_defconfig(defconfig_file)) { + fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"), + defconfig_file); + return 1; + } + } else if (input_mode != listnewconfig) { + if (conf_write(NULL)) { + fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n")); + exit(1); + } + } + return 0; +} + +/* + * Helper function to facilitate fgets() by Jean Sacren. + */ +void xfgets(char *str, int size, FILE *in) +{ + if (fgets(str, size, in) == NULL) + fprintf(stderr, "\nError in reading or end of file.\n"); +} diff --git a/tools/kconfig/confdata.c b/tools/kconfig/confdata.c new file mode 100644 index 0000000..297b079 --- /dev/null +++ b/tools/kconfig/confdata.c @@ -0,0 +1,1249 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lkc.h" + +struct conf_printer { + void (*print_symbol)(FILE *, struct symbol *, const char *, void *); + void (*print_comment)(FILE *, const char *, void *); +}; + +static void conf_warning(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static void conf_message(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +static const char *conf_filename; +static int conf_lineno, conf_warnings, conf_unsaved; + +const char conf_defname[] = "arch/$ARCH/defconfig"; + +static void conf_warning(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + conf_warnings++; +} + +static void conf_default_message_callback(const char *fmt, va_list ap) +{ + printf("#\n# "); + vprintf(fmt, ap); + printf("\n#\n"); +} + +static void (*conf_message_callback) (const char *fmt, va_list ap) = + conf_default_message_callback; +void conf_set_message_callback(void (*fn) (const char *fmt, va_list ap)) +{ + conf_message_callback = fn; +} + +static void conf_message(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (conf_message_callback) + conf_message_callback(fmt, ap); + va_end(ap); +} + +const char *conf_get_configname(void) +{ + char *name = getenv("KCONFIG_CONFIG"); + + return name ? name : ".config"; +} + +const char *conf_get_autoconfig_name(void) +{ + char *name = getenv("KCONFIG_AUTOCONFIG"); + + return name ? name : "include/config/auto.conf"; +} + +static char *conf_expand_value(const char *in) +{ + struct symbol *sym; + const char *src; + static char res_value[SYMBOL_MAXLENGTH]; + char *dst, name[SYMBOL_MAXLENGTH]; + + res_value[0] = 0; + dst = name; + while ((src = strchr(in, '$'))) { + strncat(res_value, in, src - in); + src++; + dst = name; + while (isalnum(*src) || *src == '_') + *dst++ = *src++; + *dst = 0; + sym = sym_lookup(name, 0); + sym_calc_value(sym); + strcat(res_value, sym_get_string_value(sym)); + in = src; + } + strcat(res_value, in); + + return res_value; +} + +char *conf_get_default_confname(void) +{ + struct stat buf; + static char fullname[PATH_MAX+1]; + char *env, *name; + + name = conf_expand_value(conf_defname); + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + if (!stat(fullname, &buf)) + return fullname; + } + return name; +} + +static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p) +{ + char *p2; + + switch (sym->type) { + case S_TRISTATE: + if (p[0] == 'm') { + sym->def[def].tri = mod; + sym->flags |= def_flags; + break; + } + /* fall through */ + case S_BOOLEAN: + if (p[0] == 'y') { + sym->def[def].tri = yes; + sym->flags |= def_flags; + break; + } + if (p[0] == 'n') { + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + } + if (def != S_DEF_AUTO) + conf_warning("symbol value '%s' invalid for %s", + p, sym->name); + return 1; + case S_OTHER: + if (*p != '"') { + for (p2 = p; *p2 && !isspace(*p2); p2++) + ; + sym->type = S_STRING; + goto done; + } + /* fall through */ + case S_STRING: + if (*p++ != '"') + break; + for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) { + if (*p2 == '"') { + *p2 = 0; + break; + } + memmove(p2, p2 + 1, strlen(p2)); + } + if (!p2) { + if (def != S_DEF_AUTO) + conf_warning("invalid string found"); + return 1; + } + /* fall through */ + case S_INT: + case S_HEX: + done: + if (sym_string_valid(sym, p)) { + sym->def[def].val = strdup(p); + sym->flags |= def_flags; + } else { + if (def != S_DEF_AUTO) + conf_warning("symbol value '%s' invalid for %s", + p, sym->name); + return 1; + } + break; + default: + ; + } + return 0; +} + +#define LINE_GROWTH 16 +static int add_byte(int c, char **lineptr, size_t slen, size_t *n) +{ + char *nline; + size_t new_size = slen + 1; + if (new_size > *n) { + new_size += LINE_GROWTH - 1; + new_size *= 2; + nline = realloc(*lineptr, new_size); + if (!nline) + return -1; + + *lineptr = nline; + *n = new_size; + } + + (*lineptr)[slen] = c; + + return 0; +} + +static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream) +{ + char *line = *lineptr; + size_t slen = 0; + + for (;;) { + int c = getc(stream); + + switch (c) { + case '\n': + if (add_byte(c, &line, slen, n) < 0) + goto e_out; + slen++; + /* fall through */ + case EOF: + if (add_byte('\0', &line, slen, n) < 0) + goto e_out; + *lineptr = line; + if (slen == 0) + return -1; + return slen; + default: + if (add_byte(c, &line, slen, n) < 0) + goto e_out; + slen++; + } + } + +e_out: + line[slen-1] = '\0'; + *lineptr = line; + return -1; +} + +int conf_read_simple(const char *name, int def) +{ + FILE *in = NULL; + char *line = NULL; + size_t line_asize = 0; + char *p, *p2; + struct symbol *sym; + int i, def_flags; + + if (name) { + in = zconf_fopen(name); + } else { + struct property *prop; + + name = conf_get_configname(); + in = zconf_fopen(name); + if (in) + goto load; + sym_add_change_count(1); + if (!sym_defconfig_list) + return 1; + + for_all_defaults(sym_defconfig_list, prop) { + if (expr_calc_value(prop->visible.expr) == no || + prop->expr->type != E_SYMBOL) + continue; + name = conf_expand_value(prop->expr->left.sym->name); + in = zconf_fopen(name); + if (in) { + conf_message(_("using defaults found in %s"), + name); + goto load; + } + } + } + if (!in) + return 1; + +load: + conf_filename = name; + conf_lineno = 0; + conf_warnings = 0; + conf_unsaved = 0; + + def_flags = SYMBOL_DEF << def; + for_all_symbols(i, sym) { + sym->flags |= SYMBOL_CHANGED; + sym->flags &= ~(def_flags|SYMBOL_VALID); + if (sym_is_choice(sym)) + sym->flags |= def_flags; + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + if (sym->def[def].val) + free(sym->def[def].val); + /* fall through */ + default: + sym->def[def].val = NULL; + sym->def[def].tri = no; + } + } + + while (compat_getline(&line, &line_asize, in) != -1) { + conf_lineno++; + sym = NULL; + if (line[0] == '#') { + if (memcmp(line + 2, CONFIG_, strlen(CONFIG_))) + continue; + p = strchr(line + 2 + strlen(CONFIG_), ' '); + if (!p) + continue; + *p++ = 0; + if (strncmp(p, "is not set", 10)) + continue; + if (def == S_DEF_USER) { + sym = sym_find(line + 2 + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + 2 + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_BOOLEAN; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + sym->def[def].tri = no; + sym->flags |= def_flags; + break; + default: + ; + } + } else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) { + p = strchr(line + strlen(CONFIG_), '='); + if (!p) + continue; + *p++ = 0; + p2 = strchr(p, '\n'); + if (p2) { + *p2-- = 0; + if (*p2 == '\r') + *p2 = 0; + } + if (def == S_DEF_USER) { + sym = sym_find(line + strlen(CONFIG_)); + if (!sym) { + sym_add_change_count(1); + goto setsym; + } + } else { + sym = sym_lookup(line + strlen(CONFIG_), 0); + if (sym->type == S_UNKNOWN) + sym->type = S_OTHER; + } + if (sym->flags & def_flags) { + conf_warning("override: reassigning to symbol %s", sym->name); + } + if (conf_set_sym_val(sym, def, def_flags, p)) + continue; + } else { + if (line[0] != '\r' && line[0] != '\n') + conf_warning("unexpected data: %.*s", + (int)strcspn(line, "\r\n"), line); + + continue; + } +setsym: + if (sym && sym_is_choice_value(sym)) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + switch (sym->def[def].tri) { + case no: + break; + case mod: + if (cs->def[def].tri == yes) { + conf_warning("%s creates inconsistent choice state", sym->name); + cs->flags &= ~def_flags; + } + break; + case yes: + if (cs->def[def].tri != no) + conf_warning("override: %s changes choice state", sym->name); + cs->def[def].val = sym; + break; + } + cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri); + } + } + free(line); + fclose(in); + return 0; +} + +int conf_read(const char *name) +{ + struct symbol *sym; + int i; + + sym_set_change_count(0); + + if (conf_read_simple(name, S_DEF_USER)) { + sym_calc_value(modules_sym); + return 1; + } + + sym_calc_value(modules_sym); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO)) + continue; + if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) { + /* check that calculated value agrees with saved value */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym)) + break; + if (!sym_is_choice(sym)) + continue; + /* fall through */ + default: + if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) + continue; + break; + } + } else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE)) + /* no previous value and not saved */ + continue; + conf_unsaved++; + /* maybe print value in verbose mode... */ + } + + for_all_symbols(i, sym) { + if (sym_has_value(sym) && !sym_is_choice_value(sym)) { + /* Reset values of generates values, so they'll appear + * as new, if they should become visible, but that + * doesn't quite work if the Kconfig and the saved + * configuration disagree. + */ + if (sym->visible == no && !conf_unsaved) + sym->flags &= ~SYMBOL_DEF_USER; + switch (sym->type) { + case S_STRING: + case S_INT: + case S_HEX: + /* Reset a string value if it's out of range */ + if (sym_string_within_range(sym, sym->def[S_DEF_USER].val)) + break; + sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); + conf_unsaved++; + break; + default: + break; + } + } + } + + sym_add_change_count(conf_warnings || conf_unsaved); + + return 0; +} + +/* + * Kconfig configuration printer + * + * This printer is used when generating the resulting configuration after + * kconfig invocation and `defconfig' files. Unset symbol might be omitted by + * passing a non-NULL argument to the printer. + * + */ +static void +kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (*value == 'n') { + bool skip_unset = (arg != NULL); + + if (!skip_unset) + fprintf(fp, "# %s%s is not set\n", + CONFIG_, sym->name); + return; + } + break; + default: + break; + } + + fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, value); +} + +static void +kconfig_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, "#"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } +} + +static struct conf_printer kconfig_printer_cb = +{ + .print_symbol = kconfig_print_symbol, + .print_comment = kconfig_print_comment, +}; + +/* + * Header printer + * + * This printer is used when generating the `include/generated/autoconf.h' file. + */ +static void +header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: { + const char *suffix = ""; + + switch (*value) { + case 'n': + break; + case 'm': + suffix = "_MODULE"; + /* fall through */ + default: + fprintf(fp, "#define %s%s%s 1\n", + CONFIG_, sym->name, suffix); + } + break; + } + case S_HEX: { + const char *prefix = ""; + + if (value[0] != '0' || (value[1] != 'x' && value[1] != 'X')) + prefix = "0x"; + fprintf(fp, "#define %s%s %s%s\n", + CONFIG_, sym->name, prefix, value); + break; + } + case S_STRING: + case S_INT: + fprintf(fp, "#define %s%s %s\n", + CONFIG_, sym->name, value); + break; + default: + break; + } + +} + +static void +header_print_comment(FILE *fp, const char *value, void *arg) +{ + const char *p = value; + size_t l; + + fprintf(fp, "/*\n"); + for (;;) { + l = strcspn(p, "\n"); + fprintf(fp, " *"); + if (l) { + fprintf(fp, " "); + xfwrite(p, l, 1, fp); + p += l; + } + fprintf(fp, "\n"); + if (*p++ == '\0') + break; + } + fprintf(fp, " */\n"); +} + +static struct conf_printer header_printer_cb = +{ + .print_symbol = header_print_symbol, + .print_comment = header_print_comment, +}; + +/* + * Tristate printer + * + * This printer is used when generating the `include/config/tristate.conf' file. + */ +static void +tristate_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + if (sym->type == S_TRISTATE && *value != 'n') + fprintf(fp, "%s%s=%c\n", CONFIG_, sym->name, (char)toupper(*value)); +} + +static struct conf_printer tristate_printer_cb = +{ + .print_symbol = tristate_print_symbol, + .print_comment = kconfig_print_comment, +}; + +static void conf_write_symbol(FILE *fp, struct symbol *sym, + struct conf_printer *printer, void *printer_arg) +{ + const char *str; + + switch (sym->type) { + case S_OTHER: + case S_UNKNOWN: + break; + case S_STRING: + str = sym_get_string_value(sym); + str = sym_escape_string_value(str); + printer->print_symbol(fp, sym, str, printer_arg); + free((void *)str); + break; + default: + str = sym_get_string_value(sym); + printer->print_symbol(fp, sym, str, printer_arg); + } +} + +static void +conf_write_heading(FILE *fp, struct conf_printer *printer, void *printer_arg) +{ + char buf[256]; + + snprintf(buf, sizeof(buf), + "\n" + "Automatically generated file; DO NOT EDIT.\n" + "%s\n", + rootmenu.prompt->text); + + printer->print_comment(fp, buf, printer_arg); +} + +/* + * Write out a minimal config. + * All values that has default values are skipped as this is redundant. + */ +int conf_write_defconfig(const char *filename) +{ + struct symbol *sym; + struct menu *menu; + FILE *out; + + out = fopen(filename, "w"); + if (!out) + return 1; + + sym_clear_all_valid(); + + /* Traverse all menus to find all relevant symbols */ + menu = rootmenu.list; + + while (menu != NULL) + { + sym = menu->sym; + if (sym == NULL) { + if (!menu_is_visible(menu)) + goto next_menu; + } else if (!sym_is_choice(sym)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next_menu; + sym->flags &= ~SYMBOL_WRITE; + /* If we cannot change the symbol - skip */ + if (!sym_is_changable(sym)) + goto next_menu; + /* If symbol equals to default value - skip */ + if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0) + goto next_menu; + + /* + * If symbol is a choice value and equals to the + * default for a choice - skip. + * But only if value is bool and equal to "y" and + * choice is not "optional". + * (If choice is "optional" then all values can be "n") + */ + if (sym_is_choice_value(sym)) { + struct symbol *cs; + struct symbol *ds; + + cs = prop_get_symbol(sym_get_choice_prop(sym)); + ds = sym_choice_default(cs); + if (!sym_is_optional(cs) && sym == ds) { + if ((sym->type == S_BOOLEAN) && + sym_get_tristate_value(sym) == yes) + goto next_menu; + } + } + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } +next_menu: + if (menu->list != NULL) { + menu = menu->list; + } + else if (menu->next != NULL) { + menu = menu->next; + } else { + while ((menu = menu->parent)) { + if (menu->next != NULL) { + menu = menu->next; + break; + } + } + } + } + fclose(out); + return 0; +} + +int conf_write(const char *name) +{ + FILE *out; + struct symbol *sym; + struct menu *menu; + const char *basename; + const char *str; + char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1]; + char *env; + + dirname[0] = 0; + if (name && name[0]) { + struct stat st; + char *slash; + + if (!stat(name, &st) && S_ISDIR(st.st_mode)) { + strcpy(dirname, name); + strcat(dirname, "/"); + basename = conf_get_configname(); + } else if ((slash = strrchr(name, '/'))) { + int size = slash - name + 1; + memcpy(dirname, name, size); + dirname[size] = 0; + if (slash[1]) + basename = slash + 1; + else + basename = conf_get_configname(); + } else + basename = name; + } else + basename = conf_get_configname(); + + sprintf(newname, "%s%s", dirname, basename); + env = getenv("KCONFIG_OVERWRITECONFIG"); + if (!env || !*env) { + sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(tmpname, "w"); + } else { + *tmpname = 0; + out = fopen(newname, "w"); + } + if (!out) + return 1; + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + if (!conf_get_changed()) + sym_clear_all_valid(); + + menu = rootmenu.list; + while (menu) { + sym = menu->sym; + if (!sym) { + if (!menu_is_visible(menu)) + goto next; + str = menu_get_prompt(menu); + fprintf(out, "\n" + "#\n" + "# %s\n" + "#\n", str); + } else if (!(sym->flags & SYMBOL_CHOICE)) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE)) + goto next; + sym->flags &= ~SYMBOL_WRITE; + + conf_write_symbol(out, sym, &kconfig_printer_cb, NULL); + } + +next: + if (menu->list) { + menu = menu->list; + continue; + } + if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->next) { + menu = menu->next; + break; + } + } + } + fclose(out); + + if (*tmpname) { + strcat(dirname, basename); + strcat(dirname, ".old"); + rename(newname, dirname); + if (rename(tmpname, newname)) + return 1; + } + + conf_message(_("configuration written to %s"), newname); + + sym_set_change_count(0); + + return 0; +} + +static int conf_split_config(void) +{ + const char *name; + char path[PATH_MAX+1]; + char *s, *d, c; + struct symbol *sym; + struct stat sb; + int res, i, fd; + + name = conf_get_autoconfig_name(); + conf_read_simple(name, S_DEF_AUTO); + sym_calc_value(modules_sym); + + if (chdir("include/config")) + return 1; + + res = 0; + for_all_symbols(i, sym) { + sym_calc_value(sym); + if ((sym->flags & SYMBOL_AUTO) || !sym->name) + continue; + if (sym->flags & SYMBOL_WRITE) { + if (sym->flags & SYMBOL_DEF_AUTO) { + /* + * symbol has old and new value, + * so compare them... + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == + sym->def[S_DEF_AUTO].tri) + continue; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (!strcmp(sym_get_string_value(sym), + sym->def[S_DEF_AUTO].val)) + continue; + break; + default: + break; + } + } else { + /* + * If there is no old value, only 'no' (unset) + * is allowed as new value. + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == no) + continue; + break; + default: + break; + } + } + } else if (!(sym->flags & SYMBOL_DEF_AUTO)) + /* There is neither an old nor a new value. */ + continue; + /* else + * There is an old value, but no new value ('no' (unset) + * isn't saved in auto.conf, so the old value is always + * different from 'no'). + */ + + /* Replace all '_' and append ".h" */ + s = sym->name; + d = path; + while ((c = *s++)) { + c = tolower(c); + *d++ = (c == '_') ? '/' : c; + } + strcpy(d, ".h"); + + /* Assume directory path already exists. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + if (errno != ENOENT) { + res = 1; + break; + } + /* + * Create directory components, + * unless they exist already. + */ + d = path; + while ((d = strchr(d, '/'))) { + *d = 0; + if (stat(path, &sb) && mkdir(path, 0755)) { + res = 1; + goto out; + } + *d++ = '/'; + } + /* Try it again. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + res = 1; + break; + } + } + close(fd); + } +out: + if (chdir("../..")) + return 1; + + return res; +} + +int conf_write_autoconf(void) +{ + struct symbol *sym; + const char *name; + FILE *out, *tristate, *out_h; + int i; + + sym_clear_all_valid(); + + file_write_dep("include/config/auto.conf.cmd"); + + if (conf_split_config()) + return 1; + + out = fopen(".tmpconfig", "w"); + if (!out) + return 1; + + tristate = fopen(".tmpconfig_tristate", "w"); + if (!tristate) { + fclose(out); + return 1; + } + + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) { + fclose(out); + fclose(tristate); + return 1; + } + + conf_write_heading(out, &kconfig_printer_cb, NULL); + + conf_write_heading(tristate, &tristate_printer_cb, NULL); + + conf_write_heading(out_h, &header_printer_cb, NULL); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE) || !sym->name) + continue; + + /* write symbol to auto.conf, tristate and header files */ + conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); + + conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); + + conf_write_symbol(out_h, sym, &header_printer_cb, NULL); + } + fclose(out); + fclose(tristate); + fclose(out_h); + + name = getenv("KCONFIG_AUTOHEADER"); + if (!name) + name = "include/generated/autoconf.h"; + if (rename(".tmpconfig.h", name)) + return 1; + name = getenv("KCONFIG_TRISTATE"); + if (!name) + name = "include/config/tristate.conf"; + if (rename(".tmpconfig_tristate", name)) + return 1; + name = conf_get_autoconfig_name(); + /* + * This must be the last step, kbuild has a dependency on auto.conf + * and this marks the successful completion of the previous steps. + */ + if (rename(".tmpconfig", name)) + return 1; + + return 0; +} + +static int sym_change_count; +static void (*conf_changed_callback)(void); + +void sym_set_change_count(int count) +{ + int _sym_change_count = sym_change_count; + sym_change_count = count; + if (conf_changed_callback && + (bool)_sym_change_count != (bool)count) + conf_changed_callback(); +} + +void sym_add_change_count(int count) +{ + sym_set_change_count(count + sym_change_count); +} + +bool conf_get_changed(void) +{ + return sym_change_count; +} + +void conf_set_changed_callback(void (*fn)(void)) +{ + conf_changed_callback = fn; +} + +static bool randomize_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + int cnt, def; + + /* + * If choice is mod then we may have more items selected + * and if no then no-one. + * In both cases stop. + */ + if (csym->curr.tri != yes) + return false; + + prop = sym_get_choice_prop(csym); + + /* count entries in choice block */ + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) + cnt++; + + /* + * find a random value and set it to yes, + * set the rest to no so we have only one set + */ + def = (rand() % cnt); + + cnt = 0; + expr_list_for_each_sym(prop->expr, e, sym) { + if (def == cnt++) { + sym->def[S_DEF_USER].tri = yes; + csym->def[S_DEF_USER].val = sym; + } + else { + sym->def[S_DEF_USER].tri = no; + } + sym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + sym->flags &= ~SYMBOL_VALID; + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID); + + return true; +} + +void set_all_choice_values(struct symbol *csym) +{ + struct property *prop; + struct symbol *sym; + struct expr *e; + + prop = sym_get_choice_prop(csym); + + /* + * Set all non-assinged choice values to no + */ + expr_list_for_each_sym(prop->expr, e, sym) { + if (!sym_has_value(sym)) + sym->def[S_DEF_USER].tri = no; + } + csym->flags |= SYMBOL_DEF_USER; + /* clear VALID to get value calculated */ + csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES); +} + +bool conf_set_all_new_symbols(enum conf_def_mode mode) +{ + struct symbol *sym, *csym; + int i, cnt, pby, pty, ptm; /* pby: probability of boolean = y + * pty: probability of tristate = y + * ptm: probability of tristate = m + */ + + pby = 50; pty = ptm = 33; /* can't go as the default in switch-case + * below, otherwise gcc whines about + * -Wmaybe-uninitialized */ + if (mode == def_random) { + int n, p[3]; + char *env = getenv("KCONFIG_PROBABILITY"); + n = 0; + while( env && *env ) { + char *endp; + int tmp = strtol( env, &endp, 10 ); + if( tmp >= 0 && tmp <= 100 ) { + p[n++] = tmp; + } else { + errno = ERANGE; + perror( "KCONFIG_PROBABILITY" ); + exit( 1 ); + } + env = (*endp == ':') ? endp+1 : endp; + if( n >=3 ) { + break; + } + } + switch( n ) { + case 1: + pby = p[0]; ptm = pby/2; pty = pby-ptm; + break; + case 2: + pty = p[0]; ptm = p[1]; pby = pty + ptm; + break; + case 3: + pby = p[0]; pty = p[1]; ptm = p[2]; + break; + } + + if( pty+ptm > 100 ) { + errno = ERANGE; + perror( "KCONFIG_PROBABILITY" ); + exit( 1 ); + } + } + bool has_changed = false; + + for_all_symbols(i, sym) { + if (sym_has_value(sym) || (sym->flags & SYMBOL_VALID)) + continue; + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + has_changed = true; + switch (mode) { + case def_yes: + sym->def[S_DEF_USER].tri = yes; + break; + case def_mod: + sym->def[S_DEF_USER].tri = mod; + break; + case def_no: + if (sym->flags & SYMBOL_ALLNOCONFIG_Y) + sym->def[S_DEF_USER].tri = yes; + else + sym->def[S_DEF_USER].tri = no; + break; + case def_random: + sym->def[S_DEF_USER].tri = no; + cnt = rand() % 100; + if (sym->type == S_TRISTATE) { + if (cnt < pty) + sym->def[S_DEF_USER].tri = yes; + else if (cnt < (pty+ptm)) + sym->def[S_DEF_USER].tri = mod; + } else if (cnt < pby) + sym->def[S_DEF_USER].tri = yes; + break; + default: + continue; + } + if (!(sym_is_choice(sym) && mode == def_random)) + sym->flags |= SYMBOL_DEF_USER; + break; + default: + break; + } + + } + + sym_clear_all_valid(); + + /* + * We have different type of choice blocks. + * If curr.tri equals to mod then we can select several + * choice symbols in one block. + * In this case we do nothing. + * If curr.tri equals yes then only one symbol can be + * selected in a choice block and we set it to yes, + * and the rest to no. + */ + if (mode != def_random) { + for_all_symbols(i, csym) { + if ((sym_is_choice(csym) && !sym_has_value(csym)) || + sym_is_choice_value(csym)) + csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES; + } + } + + for_all_symbols(i, csym) { + if (sym_has_value(csym) || !sym_is_choice(csym)) + continue; + + sym_calc_value(csym); + if (mode == def_random) + has_changed = randomize_choice_values(csym); + else { + set_all_choice_values(csym); + has_changed = true; + } + } + + return has_changed; +} diff --git a/tools/kconfig/expr.c b/tools/kconfig/expr.c new file mode 100644 index 0000000..cbf4996 --- /dev/null +++ b/tools/kconfig/expr.c @@ -0,0 +1,1206 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include + +#include "lkc.h" + +#define DEBUG_EXPR 0 + +static int expr_eq(struct expr *e1, struct expr *e2); +static struct expr *expr_eliminate_yn(struct expr *e); + +struct expr *expr_alloc_symbol(struct symbol *sym) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = E_SYMBOL; + e->left.sym = sym; + return e; +} + +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.expr = ce; + return e; +} + +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.expr = e1; + e->right.expr = e2; + return e; +} + +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) +{ + struct expr *e = xcalloc(1, sizeof(*e)); + e->type = type; + e->left.sym = s1; + e->right.sym = s2; + return e; +} + +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_AND, e1, e2) : e1; +} + +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2) +{ + if (!e1) + return e2; + return e2 ? expr_alloc_two(E_OR, e1, e2) : e1; +} + +struct expr *expr_copy(const struct expr *org) +{ + struct expr *e; + + if (!org) + return NULL; + + e = xmalloc(sizeof(*org)); + memcpy(e, org, sizeof(*org)); + switch (org->type) { + case E_SYMBOL: + e->left = org->left; + break; + case E_NOT: + e->left.expr = expr_copy(org->left.expr); + break; + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + e->left.sym = org->left.sym; + e->right.sym = org->right.sym; + break; + case E_AND: + case E_OR: + case E_LIST: + e->left.expr = expr_copy(org->left.expr); + e->right.expr = expr_copy(org->right.expr); + break; + default: + printf("can't copy type %d\n", e->type); + free(e); + e = NULL; + break; + } + + return e; +} + +void expr_free(struct expr *e) +{ + if (!e) + return; + + switch (e->type) { + case E_SYMBOL: + break; + case E_NOT: + expr_free(e->left.expr); + return; + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + break; + case E_OR: + case E_AND: + expr_free(e->left.expr); + expr_free(e->right.expr); + break; + default: + printf("how to free type %d?\n", e->type); + break; + } + free(e); +} + +static int trans_count; + +#define e1 (*ep1) +#define e2 (*ep2) + +static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ + if (e1->type == type) { + __expr_eliminate_eq(type, &e1->left.expr, &e2); + __expr_eliminate_eq(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + __expr_eliminate_eq(type, &e1, &e2->left.expr); + __expr_eliminate_eq(type, &e1, &e2->right.expr); + return; + } + if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym && + (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) + return; + if (!expr_eq(e1, e2)) + return; + trans_count++; + expr_free(e1); expr_free(e2); + switch (type) { + case E_OR: + e1 = expr_alloc_symbol(&symbol_no); + e2 = expr_alloc_symbol(&symbol_no); + break; + case E_AND: + e1 = expr_alloc_symbol(&symbol_yes); + e2 = expr_alloc_symbol(&symbol_yes); + break; + default: + ; + } +} + +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) +{ + if (!e1 || !e2) + return; + switch (e1->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e1->type, ep1, ep2); + default: + ; + } + if (e1->type != e2->type) switch (e2->type) { + case E_OR: + case E_AND: + __expr_eliminate_eq(e2->type, ep1, ep2); + default: + ; + } + e1 = expr_eliminate_yn(e1); + e2 = expr_eliminate_yn(e2); +} + +#undef e1 +#undef e2 + +static int expr_eq(struct expr *e1, struct expr *e2) +{ + int res, old_count; + + if (e1->type != e2->type) + return 0; + switch (e1->type) { + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym; + case E_SYMBOL: + return e1->left.sym == e2->left.sym; + case E_NOT: + return expr_eq(e1->left.expr, e2->left.expr); + case E_AND: + case E_OR: + e1 = expr_copy(e1); + e2 = expr_copy(e2); + old_count = trans_count; + expr_eliminate_eq(&e1, &e2); + res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL && + e1->left.sym == e2->left.sym); + expr_free(e1); + expr_free(e2); + trans_count = old_count; + return res; + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + + if (DEBUG_EXPR) { + expr_fprint(e1, stdout); + printf(" = "); + expr_fprint(e2, stdout); + printf(" ?\n"); + } + + return 0; +} + +static struct expr *expr_eliminate_yn(struct expr *e) +{ + struct expr *tmp; + + if (e) switch (e->type) { + case E_AND: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.expr = NULL; + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } + } + break; + case E_OR: + e->left.expr = expr_eliminate_yn(e->left.expr); + e->right.expr = expr_eliminate_yn(e->right.expr); + if (e->left.expr->type == E_SYMBOL) { + if (e->left.expr->left.sym == &symbol_no) { + free(e->left.expr); + tmp = e->right.expr; + *e = *(e->right.expr); + free(tmp); + return e; + } else if (e->left.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + if (e->right.expr->type == E_SYMBOL) { + if (e->right.expr->left.sym == &symbol_no) { + free(e->right.expr); + tmp = e->left.expr; + *e = *(e->left.expr); + free(tmp); + return e; + } else if (e->right.expr->left.sym == &symbol_yes) { + expr_free(e->left.expr); + expr_free(e->right.expr); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.expr = NULL; + return e; + } + } + break; + default: + ; + } + return e; +} + +/* + * bool FOO!=n => FOO + */ +struct expr *expr_trans_bool(struct expr *e) +{ + if (!e) + return NULL; + switch (e->type) { + case E_AND: + case E_OR: + case E_NOT: + e->left.expr = expr_trans_bool(e->left.expr); + e->right.expr = expr_trans_bool(e->right.expr); + break; + case E_UNEQUAL: + // FOO!=n -> FOO + if (e->left.sym->type == S_TRISTATE) { + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + } + } + break; + default: + ; + } + return e; +} + +/* + * e1 || e2 -> ? + */ +static struct expr *expr_join_or(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='m') -> (a!='n') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) { + // (a='y') || (a='n') -> (a!='m') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod); + } + if (e1->type == E_EQUAL && e2->type == E_EQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) { + // (a='m') || (a='n') -> (a!='y') + return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes); + } + } + if (sym1->type == S_BOOLEAN && sym1 == sym2) { + if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) || + (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL)) + return expr_alloc_symbol(&symbol_yes); + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") || ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static struct expr *expr_join_and(struct expr *e1, struct expr *e2) +{ + struct expr *tmp; + struct symbol *sym1, *sym2; + + if (expr_eq(e1, e2)) + return expr_copy(e1); + if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT) + return NULL; + if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT) + return NULL; + if (e1->type == E_NOT) { + tmp = e1->left.expr; + if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL) + return NULL; + sym1 = tmp->left.sym; + } else + sym1 = e1->left.sym; + if (e2->type == E_NOT) { + if (e2->left.expr->type != E_SYMBOL) + return NULL; + sym2 = e2->left.expr->left.sym; + } else + sym2 = e2->left.sym; + if (sym1 != sym2) + return NULL; + if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE) + return NULL; + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes)) + // (a) && (a='y') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no)) + // (a) && (a!='n') -> (a) + return expr_alloc_symbol(sym1); + + if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod)) + // (a) && (a!='m') -> (a='y') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if (sym1->type == S_TRISTATE) { + if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e1->right.sym; + if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) { + // (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b' + sym2 = e2->right.sym; + if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST)) + return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2) + : expr_alloc_symbol(&symbol_no); + } + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) || + (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) + // (a!='y') && (a!='m') -> (a='n') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_no); + + if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL && + ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) || + (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) + // (a!='m') && (a!='n') -> (a='m') + return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes); + + if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) || + (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) || + (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) || + (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes)) + return NULL; + } + + if (DEBUG_EXPR) { + printf("optimize ("); + expr_fprint(e1, stdout); + printf(") && ("); + expr_fprint(e2, stdout); + printf(")?\n"); + } + return NULL; +} + +static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2) +{ +#define e1 (*ep1) +#define e2 (*ep2) + struct expr *tmp; + + if (e1->type == type) { + expr_eliminate_dups1(type, &e1->left.expr, &e2); + expr_eliminate_dups1(type, &e1->right.expr, &e2); + return; + } + if (e2->type == type) { + expr_eliminate_dups1(type, &e1, &e2->left.expr); + expr_eliminate_dups1(type, &e1, &e2->right.expr); + return; + } + if (e1 == e2) + return; + + switch (e1->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e1->type, &e1, &e1); + default: + ; + } + + switch (type) { + case E_OR: + tmp = expr_join_or(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_no); + e2 = tmp; + trans_count++; + } + break; + case E_AND: + tmp = expr_join_and(e1, e2); + if (tmp) { + expr_free(e1); expr_free(e2); + e1 = expr_alloc_symbol(&symbol_yes); + e2 = tmp; + trans_count++; + } + break; + default: + ; + } +#undef e1 +#undef e2 +} + +struct expr *expr_eliminate_dups(struct expr *e) +{ + int oldcount; + if (!e) + return e; + + oldcount = trans_count; + while (1) { + trans_count = 0; + switch (e->type) { + case E_OR: case E_AND: + expr_eliminate_dups1(e->type, &e, &e); + default: + ; + } + if (!trans_count) + break; + e = expr_eliminate_yn(e); + } + trans_count = oldcount; + return e; +} + +struct expr *expr_transform(struct expr *e) +{ + struct expr *tmp; + + if (!e) + return NULL; + switch (e->type) { + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + case E_SYMBOL: + case E_LIST: + break; + default: + e->left.expr = expr_transform(e->left.expr); + e->right.expr = expr_transform(e->right.expr); + } + + switch (e->type) { + case E_EQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + break; + case E_UNEQUAL: + if (e->left.sym->type != S_BOOLEAN) + break; + if (e->right.sym == &symbol_no) { + e->type = E_SYMBOL; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_mod) { + printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + e->right.sym = NULL; + break; + } + if (e->right.sym == &symbol_yes) { + e->type = E_NOT; + e->left.expr = expr_alloc_symbol(e->left.sym); + e->right.sym = NULL; + break; + } + break; + case E_NOT: + switch (e->left.expr->type) { + case E_NOT: + // !!a -> a + tmp = e->left.expr->left.expr; + free(e->left.expr); + free(e); + e = tmp; + e = expr_transform(e); + break; + case E_EQUAL: + case E_UNEQUAL: + // !a='x' -> a!='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; + break; + case E_LEQ: + case E_GEQ: + // !a<='x' -> a>'x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_LEQ ? E_GTH : E_LTH; + break; + case E_LTH: + case E_GTH: + // !a<'x' -> a>='x' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = e->type == E_LTH ? E_GEQ : E_LEQ; + break; + case E_OR: + // !(a || b) -> !a && !b + tmp = e->left.expr; + e->type = E_AND; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_AND: + // !(a && b) -> !a || !b + tmp = e->left.expr; + e->type = E_OR; + e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); + tmp->type = E_NOT; + tmp->right.expr = NULL; + e = expr_transform(e); + break; + case E_SYMBOL: + if (e->left.expr->left.sym == &symbol_yes) { + // !'y' -> 'n' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_no; + break; + } + if (e->left.expr->left.sym == &symbol_mod) { + // !'m' -> 'm' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_mod; + break; + } + if (e->left.expr->left.sym == &symbol_no) { + // !'n' -> 'y' + tmp = e->left.expr; + free(e); + e = tmp; + e->type = E_SYMBOL; + e->left.sym = &symbol_yes; + break; + } + break; + default: + ; + } + break; + default: + ; + } + return e; +} + +int expr_contains_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return 0; + + switch (dep->type) { + case E_AND: + case E_OR: + return expr_contains_symbol(dep->left.expr, sym) || + expr_contains_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + return dep->left.sym == sym || + dep->right.sym == sym; + case E_NOT: + return expr_contains_symbol(dep->left.expr, sym); + default: + ; + } + return 0; +} + +bool expr_depends_symbol(struct expr *dep, struct symbol *sym) +{ + if (!dep) + return false; + + switch (dep->type) { + case E_AND: + return expr_depends_symbol(dep->left.expr, sym) || + expr_depends_symbol(dep->right.expr, sym); + case E_SYMBOL: + return dep->left.sym == sym; + case E_EQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod) + return true; + } + break; + case E_UNEQUAL: + if (dep->left.sym == sym) { + if (dep->right.sym == &symbol_no) + return true; + } + break; + default: + ; + } + return false; +} + +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym) +{ + struct expr *e1, *e2; + + if (!e) { + e = expr_alloc_symbol(sym); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + } + switch (e->type) { + case E_AND: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_AND, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_OR, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_OR: + e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym); + e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym); + if (sym == &symbol_yes) + e = expr_alloc_two(E_OR, e1, e2); + if (sym == &symbol_no) + e = expr_alloc_two(E_AND, e1, e2); + if (type == E_UNEQUAL) + e = expr_alloc_one(E_NOT, e); + return e; + case E_NOT: + return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym); + case E_UNEQUAL: + case E_LTH: + case E_LEQ: + case E_GTH: + case E_GEQ: + case E_EQUAL: + if (type == E_EQUAL) { + if (sym == &symbol_yes) + return expr_copy(e); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_no); + if (sym == &symbol_no) + return expr_alloc_one(E_NOT, expr_copy(e)); + } else { + if (sym == &symbol_yes) + return expr_alloc_one(E_NOT, expr_copy(e)); + if (sym == &symbol_mod) + return expr_alloc_symbol(&symbol_yes); + if (sym == &symbol_no) + return expr_copy(e); + } + break; + case E_SYMBOL: + return expr_alloc_comp(type, e->left.sym, sym); + case E_LIST: + case E_RANGE: + case E_NONE: + /* panic */; + } + return NULL; +} + +enum string_value_kind { + k_string, + k_signed, + k_unsigned, + k_invalid +}; + +union string_value { + unsigned long long u; + signed long long s; +}; + +static enum string_value_kind expr_parse_string(const char *str, + enum symbol_type type, + union string_value *val) +{ + char *tail; + enum string_value_kind kind; + + errno = 0; + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + return k_string; + case S_INT: + val->s = strtoll(str, &tail, 10); + kind = k_signed; + break; + case S_HEX: + val->u = strtoull(str, &tail, 16); + kind = k_unsigned; + break; + case S_STRING: + case S_UNKNOWN: + val->s = strtoll(str, &tail, 0); + kind = k_signed; + break; + default: + return k_invalid; + } + return !errno && !*tail && tail > str && isxdigit(tail[-1]) + ? kind : k_string; +} + +tristate expr_calc_value(struct expr *e) +{ + tristate val1, val2; + const char *str1, *str2; + enum string_value_kind k1 = k_string, k2 = k_string; + union string_value lval = {}, rval = {}; + int res; + + if (!e) + return yes; + + switch (e->type) { + case E_SYMBOL: + sym_calc_value(e->left.sym); + return e->left.sym->curr.tri; + case E_AND: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_AND(val1, val2); + case E_OR: + val1 = expr_calc_value(e->left.expr); + val2 = expr_calc_value(e->right.expr); + return EXPR_OR(val1, val2); + case E_NOT: + val1 = expr_calc_value(e->left.expr); + return EXPR_NOT(val1); + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + break; + default: + printf("expr_calc_value: %d?\n", e->type); + return no; + } + + sym_calc_value(e->left.sym); + sym_calc_value(e->right.sym); + str1 = sym_get_string_value(e->left.sym); + str2 = sym_get_string_value(e->right.sym); + + if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) { + k1 = expr_parse_string(str1, e->left.sym->type, &lval); + k2 = expr_parse_string(str2, e->right.sym->type, &rval); + } + + if (k1 == k_string || k2 == k_string) + res = strcmp(str1, str2); + else if (k1 == k_invalid || k2 == k_invalid) { + if (e->type != E_EQUAL && e->type != E_UNEQUAL) { + printf("Cannot compare \"%s\" and \"%s\"\n", str1, str2); + return no; + } + res = strcmp(str1, str2); + } else if (k1 == k_unsigned || k2 == k_unsigned) + res = (lval.u > rval.u) - (lval.u < rval.u); + else /* if (k1 == k_signed && k2 == k_signed) */ + res = (lval.s > rval.s) - (lval.s < rval.s); + + switch(e->type) { + case E_EQUAL: + return res ? no : yes; + case E_GEQ: + return res >= 0 ? yes : no; + case E_GTH: + return res > 0 ? yes : no; + case E_LEQ: + return res <= 0 ? yes : no; + case E_LTH: + return res < 0 ? yes : no; + case E_UNEQUAL: + return res ? yes : no; + default: + printf("expr_calc_value: relation %d?\n", e->type); + return no; + } +} + +static int expr_compare_type(enum expr_type t1, enum expr_type t2) +{ + if (t1 == t2) + return 0; + switch (t1) { + case E_LEQ: + case E_LTH: + case E_GEQ: + case E_GTH: + if (t2 == E_EQUAL || t2 == E_UNEQUAL) + return 1; + case E_EQUAL: + case E_UNEQUAL: + if (t2 == E_NOT) + return 1; + case E_NOT: + if (t2 == E_AND) + return 1; + case E_AND: + if (t2 == E_OR) + return 1; + case E_OR: + if (t2 == E_LIST) + return 1; + case E_LIST: + if (t2 == 0) + return 1; + default: + return -1; + } + printf("[%dgt%d?]", t1, t2); + return 0; +} + +static inline struct expr * +expr_get_leftmost_symbol(const struct expr *e) +{ + + if (e == NULL) + return NULL; + + while (e->type != E_SYMBOL) + e = e->left.expr; + + return expr_copy(e); +} + +/* + * Given expression `e1' and `e2', returns the leaf of the longest + * sub-expression of `e1' not containing 'e2. + */ +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2) +{ + struct expr *ret; + + switch (e1->type) { + case E_OR: + return expr_alloc_and( + expr_simplify_unmet_dep(e1->left.expr, e2), + expr_simplify_unmet_dep(e1->right.expr, e2)); + case E_AND: { + struct expr *e; + e = expr_alloc_and(expr_copy(e1), expr_copy(e2)); + e = expr_eliminate_dups(e); + ret = (!expr_eq(e, e1)) ? e1 : NULL; + expr_free(e); + break; + } + default: + ret = e1; + break; + } + + return expr_get_leftmost_symbol(ret); +} + +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) +{ + if (!e) { + fn(data, NULL, "y"); + return; + } + + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, "("); + switch (e->type) { + case E_SYMBOL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, ""); + break; + case E_NOT: + fn(data, NULL, "!"); + expr_print(e->left.expr, fn, data, E_NOT); + break; + case E_EQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, ""); + fn(data, NULL, "="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_LEQ: + case E_LTH: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, ""); + fn(data, NULL, e->type == E_LEQ ? "<=" : "<"); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_GEQ: + case E_GTH: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, ""); + fn(data, NULL, e->type == E_GEQ ? ">=" : ">"); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_UNEQUAL: + if (e->left.sym->name) + fn(data, e->left.sym, e->left.sym->name); + else + fn(data, NULL, ""); + fn(data, NULL, "!="); + fn(data, e->right.sym, e->right.sym->name); + break; + case E_OR: + expr_print(e->left.expr, fn, data, E_OR); + fn(data, NULL, " || "); + expr_print(e->right.expr, fn, data, E_OR); + break; + case E_AND: + expr_print(e->left.expr, fn, data, E_AND); + fn(data, NULL, " && "); + expr_print(e->right.expr, fn, data, E_AND); + break; + case E_LIST: + fn(data, e->right.sym, e->right.sym->name); + if (e->left.expr) { + fn(data, NULL, " ^ "); + expr_print(e->left.expr, fn, data, E_LIST); + } + break; + case E_RANGE: + fn(data, NULL, "["); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, " "); + fn(data, e->right.sym, e->right.sym->name); + fn(data, NULL, "]"); + break; + default: + { + char buf[32]; + sprintf(buf, "", e->type); + fn(data, NULL, buf); + break; + } + } + if (expr_compare_type(prevtoken, e->type) > 0) + fn(data, NULL, ")"); +} + +static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) +{ + xfwrite(str, strlen(str), 1, data); +} + +void expr_fprint(struct expr *e, FILE *out) +{ + expr_print(e, expr_print_file_helper, out, E_NONE); +} + +static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) +{ + struct gstr *gs = (struct gstr*)data; + const char *sym_str = NULL; + + if (sym) + sym_str = sym_get_string_value(sym); + + if (gs->max_width) { + unsigned extra_length = strlen(str); + const char *last_cr = strrchr(gs->s, '\n'); + unsigned last_line_length; + + if (sym_str) + extra_length += 4 + strlen(sym_str); + + if (!last_cr) + last_cr = gs->s; + + last_line_length = strlen(gs->s) - (last_cr - gs->s); + + if ((last_line_length + extra_length) > gs->max_width) + str_append(gs, "\\\n"); + } + + str_append(gs, str); + if (sym && sym->type != S_UNKNOWN) + str_printf(gs, " [=%s]", sym_str); +} + +void expr_gstr_print(struct expr *e, struct gstr *gs) +{ + expr_print(e, expr_print_gstr_helper, gs, E_NONE); +} diff --git a/tools/kconfig/expr.h b/tools/kconfig/expr.h new file mode 100644 index 0000000..a73f762 --- /dev/null +++ b/tools/kconfig/expr.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef EXPR_H +#define EXPR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "list.h" +#ifndef __cplusplus +#include +#endif + +struct file { + struct file *next; + struct file *parent; + const char *name; + int lineno; +}; + +typedef enum tristate { + no, mod, yes +} tristate; + +enum expr_type { + E_NONE, E_OR, E_AND, E_NOT, + E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ, + E_LIST, E_SYMBOL, E_RANGE +}; + +union expr_data { + struct expr *expr; + struct symbol *sym; +}; + +struct expr { + enum expr_type type; + union expr_data left, right; +}; + +#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2)) +#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) +#define EXPR_NOT(dep) (2-(dep)) + +#define expr_list_for_each_sym(l, e, s) \ + for (e = (l); e && (s = e->right.sym); e = e->left.expr) + +struct expr_value { + struct expr *expr; + tristate tri; +}; + +struct symbol_value { + void *val; + tristate tri; +}; + +enum symbol_type { + S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER +}; + +/* enum values are used as index to symbol.def[] */ +enum { + S_DEF_USER, /* main user value */ + S_DEF_AUTO, /* values read from auto.conf */ + S_DEF_DEF3, /* Reserved for UI usage */ + S_DEF_DEF4, /* Reserved for UI usage */ + S_DEF_COUNT +}; + +struct symbol { + struct symbol *next; + char *name; + enum symbol_type type; + struct symbol_value curr; + struct symbol_value def[S_DEF_COUNT]; + tristate visible; + int flags; + struct property *prop; + struct expr_value dir_dep; + struct expr_value rev_dep; + struct expr_value implied; +}; + +#define for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) + +#define SYMBOL_CONST 0x0001 /* symbol is const */ +#define SYMBOL_CHECK 0x0008 /* used during dependency checking */ +#define SYMBOL_CHOICE 0x0010 /* start of a choice block (null name) */ +#define SYMBOL_CHOICEVAL 0x0020 /* used as a value in a choice block */ +#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */ +#define SYMBOL_OPTIONAL 0x0100 /* choice is optional - values can be 'n' */ +#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */ +#define SYMBOL_CHANGED 0x0400 /* ? */ +#define SYMBOL_AUTO 0x1000 /* value from environment variable */ +#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */ +#define SYMBOL_WARNED 0x8000 /* warning has been issued */ + +/* Set when symbol.def[] is used */ +#define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */ +#define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */ +#define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */ +#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */ +#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */ + +/* choice values need to be set before calculating this symbol value */ +#define SYMBOL_NEED_SET_CHOICE_VALUES 0x100000 + +/* Set symbol to y if allnoconfig; used for symbols that hide others */ +#define SYMBOL_ALLNOCONFIG_Y 0x200000 + +#define SYMBOL_MAXLENGTH 256 +#define SYMBOL_HASHSIZE 9973 + +/* A property represent the config options that can be associated + * with a config "symbol". + * Sample: + * config FOO + * default y + * prompt "foo prompt" + * select BAR + * config BAZ + * int "BAZ Value" + * range 1..255 + */ +enum prop_type { + P_UNKNOWN, + P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */ + P_COMMENT, /* text associated with a comment */ + P_MENU, /* prompt associated with a menuconfig option */ + P_DEFAULT, /* default y */ + P_CHOICE, /* choice value */ + P_SELECT, /* select BAR */ + P_IMPLY, /* imply BAR */ + P_RANGE, /* range 7..100 (for a symbol) */ + P_ENV, /* value from environment variable */ + P_SYMBOL, /* where a symbol is defined */ +}; + +struct property { + struct property *next; /* next property - null if last */ + struct symbol *sym; /* the symbol for which the property is associated */ + enum prop_type type; /* type of property */ + const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */ + struct expr_value visible; + struct expr *expr; /* the optional conditional part of the property */ + struct menu *menu; /* the menu the property are associated with + * valid for: P_SELECT, P_RANGE, P_CHOICE, + * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */ + struct file *file; /* what file was this property defined */ + int lineno; /* what lineno was this property defined */ +}; + +#define for_all_properties(sym, st, tok) \ + for (st = sym->prop; st; st = st->next) \ + if (st->type == (tok)) +#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT) +#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE) +#define for_all_prompts(sym, st) \ + for (st = sym->prop; st; st = st->next) \ + if (st->text) + +struct menu { + struct menu *next; + struct menu *parent; + struct menu *list; + struct symbol *sym; + struct property *prompt; + struct expr *visibility; + struct expr *dep; + unsigned int flags; + char *help; + struct file *file; + int lineno; + void *data; +}; + +#define MENU_CHANGED 0x0001 +#define MENU_ROOT 0x0002 + +struct jump_key { + struct list_head entries; + size_t offset; + struct menu *target; + int index; +}; + +#define JUMP_NB 9 + +extern struct file *file_list; +extern struct file *current_file; +struct file *lookup_file(const char *name); + +extern struct symbol symbol_yes, symbol_no, symbol_mod; +extern struct symbol *modules_sym; +extern struct symbol *sym_defconfig_list; +extern int cdebug; +struct expr *expr_alloc_symbol(struct symbol *sym); +struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); +struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2); +struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2); +struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); +struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); +struct expr *expr_copy(const struct expr *org); +void expr_free(struct expr *e); +void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); +tristate expr_calc_value(struct expr *e); +struct expr *expr_trans_bool(struct expr *e); +struct expr *expr_eliminate_dups(struct expr *e); +struct expr *expr_transform(struct expr *e); +int expr_contains_symbol(struct expr *dep, struct symbol *sym); +bool expr_depends_symbol(struct expr *dep, struct symbol *sym); +struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); +struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); + +void expr_fprint(struct expr *e, FILE *out); +struct gstr; /* forward */ +void expr_gstr_print(struct expr *e, struct gstr *gs); + +static inline int expr_is_yes(struct expr *e) +{ + return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes); +} + +static inline int expr_is_no(struct expr *e) +{ + return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no); +} + +#ifdef __cplusplus +} +#endif + +#endif /* EXPR_H */ diff --git a/tools/kconfig/gconf.c b/tools/kconfig/gconf.c new file mode 100644 index 0000000..cfddddb --- /dev/null +++ b/tools/kconfig/gconf.c @@ -0,0 +1,1521 @@ +/* Hey EMACS -*- linux-c -*- */ +/* + * + * Copyright (C) 2002-2003 Romain Lievin + * Released under the terms of the GNU GPL v2.0. + * + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "lkc.h" +#include "images.c" + +#include +#include +#include +#include + +#include +#include +#include +#include + +//#define DEBUG + +enum { + SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW +}; + +enum { + OPT_NORMAL, OPT_ALL, OPT_PROMPT +}; + +static gint view_mode = FULL_VIEW; +static gboolean show_name = TRUE; +static gboolean show_range = TRUE; +static gboolean show_value = TRUE; +static gboolean resizeable = FALSE; +static int opt_mode = OPT_NORMAL; + +GtkWidget *main_wnd = NULL; +GtkWidget *tree1_w = NULL; // left frame +GtkWidget *tree2_w = NULL; // right frame +GtkWidget *text_w = NULL; +GtkWidget *hpaned = NULL; +GtkWidget *vpaned = NULL; +GtkWidget *back_btn = NULL; +GtkWidget *save_btn = NULL; +GtkWidget *save_menu_item = NULL; + +GtkTextTag *tag1, *tag2; +GdkColor color; + +GtkTreeStore *tree1, *tree2, *tree; +GtkTreeModel *model1, *model2; +static GtkTreeIter *parents[256]; +static gint indent; + +static struct menu *current; // current node for SINGLE view +static struct menu *browsed; // browsed node for SPLIT view + +enum { + COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, + COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF, + COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD, + COL_NUMBER +}; + +static void display_list(void); +static void display_tree(struct menu *menu); +static void display_tree_part(void); +static void update_tree(struct menu *src, GtkTreeIter * dst); +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row); +static gchar **fill_row(struct menu *menu); +static void conf_changed(void); + +/* Helping/Debugging Functions */ + +const char *dbg_sym_flags(int val) +{ + static char buf[256]; + + bzero(buf, 256); + + if (val & SYMBOL_CONST) + strcat(buf, "const/"); + if (val & SYMBOL_CHECK) + strcat(buf, "check/"); + if (val & SYMBOL_CHOICE) + strcat(buf, "choice/"); + if (val & SYMBOL_CHOICEVAL) + strcat(buf, "choiceval/"); + if (val & SYMBOL_VALID) + strcat(buf, "valid/"); + if (val & SYMBOL_OPTIONAL) + strcat(buf, "optional/"); + if (val & SYMBOL_WRITE) + strcat(buf, "write/"); + if (val & SYMBOL_CHANGED) + strcat(buf, "changed/"); + if (val & SYMBOL_AUTO) + strcat(buf, "auto/"); + + buf[strlen(buf) - 1] = '\0'; + + return buf; +} + +void replace_button_icon(GladeXML * xml, GdkDrawable * window, + GtkStyle * style, gchar * btn_name, gchar ** xpm) +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkToolButton *button; + GtkWidget *image; + + pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, + &style->bg[GTK_STATE_NORMAL], + xpm); + + button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(button, image); +} + +/* Main Window Initialization */ +void init_main_window(const gchar * glade_file) +{ + GladeXML *xml; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + GtkStyle *style; + + xml = glade_xml_new(glade_file, "window1", NULL); + if (!xml) + g_error(_("GUI loading failed !\n")); + glade_xml_signal_autoconnect(xml); + + main_wnd = glade_xml_get_widget(xml, "window1"); + hpaned = glade_xml_get_widget(xml, "hpaned1"); + vpaned = glade_xml_get_widget(xml, "vpaned1"); + tree1_w = glade_xml_get_widget(xml, "treeview1"); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + text_w = glade_xml_get_widget(xml, "textview3"); + + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = glade_xml_get_widget(xml, "show_name1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = glade_xml_get_widget(xml, "show_range1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = glade_xml_get_widget(xml, "show_data1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + save_btn = glade_xml_get_widget(xml, "button3"); + save_menu_item = glade_xml_get_widget(xml, "save1"); + conf_set_changed_callback(conf_changed); + + style = gtk_widget_get_style(main_wnd); + widget = glade_xml_get_widget(xml, "toolbar1"); + + replace_button_icon(xml, main_wnd->window, style, + "button4", (gchar **) xpm_single_view); + replace_button_icon(xml, main_wnd->window, style, + "button5", (gchar **) xpm_split_view); + replace_button_icon(xml, main_wnd->window, style, + "button6", (gchar **) xpm_tree_view); + + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); + + gtk_widget_show(main_wnd); +} + +void init_tree_model(void) +{ + gint i; + + tree = tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model2 = GTK_TREE_MODEL(tree2); + + for (parents[0] = NULL, i = 1; i < 256; i++) + parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model1 = GTK_TREE_MODEL(tree1); +} + +void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + + gtk_tree_view_set_model(view, model1); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, TRUE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + gtk_widget_realize(tree1_w); +} + +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data); + +void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + gint i; + + gtk_tree_view_set_model(view, model2); + gtk_tree_view_set_headers_visible(view, TRUE); + gtk_tree_view_set_rules_hint(view, TRUE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, _("Options")); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Name"), renderer, + "text", COL_NAME, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + _("Value"), renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-gdk", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), NULL); + + column = gtk_tree_view_get_column(view, COL_NAME); + gtk_tree_view_column_set_visible(column, show_name); + column = gtk_tree_view_get_column(view, COL_NO); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_MOD); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_YES); + gtk_tree_view_column_set_visible(column, show_range); + column = gtk_tree_view_get_column(view, COL_VALUE); + gtk_tree_view_column_set_visible(column, show_value); + + if (resizeable) { + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + + +/* Utility Functions */ + + +static void text_insert_help(struct menu *menu) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *prompt = _(menu_get_prompt(menu)); + struct gstr help = str_new(); + + menu_get_ext_help(menu, &help); + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2, + NULL); + str_free(&help); +} + + +static void text_insert_msg(const char *title, const char *message) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + const char *msg = message; + + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + gtk_text_buffer_get_bounds(buffer, &start, &end); + gtk_text_buffer_delete(buffer, &start, &end); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, + NULL); + gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, + NULL); +} + + +/* Main Windows Callbacks */ + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); +gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, + gpointer user_data) +{ + GtkWidget *dialog, *label; + gint result; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons(_("Warning !"), + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_OK, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new(_("\nSave configuration ?\n")); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + return FALSE; + case GTK_RESPONSE_NO: + return FALSE; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + gtk_widget_destroy(dialog); + return TRUE; + } + + return FALSE; +} + + +void on_window1_destroy(GtkObject * object, gpointer user_data) +{ + gtk_main_quit(); +} + + +void +on_window1_size_request(GtkWidget * widget, + GtkRequisition * requisition, gpointer user_data) +{ + static gint old_h; + gint w, h; + + if (widget->window == NULL) + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + else + gdk_window_get_size(widget->window, &w, &h); + + if (h == old_h) + return; + old_h = h; + + gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); +} + + +/* Menu & Toolbar Callbacks */ + + +static void +load_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_read(fn)) + text_insert_msg(_("Error"), _("Unable to load configuration !")); + else + display_tree(&rootmenu); +} + +void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Load file...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(load_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (conf_write(NULL)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); +} + + +static void +store_filename(GtkFileSelection * file_selector, gpointer user_data) +{ + const gchar *fn; + + fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION + (user_data)); + + if (conf_write(fn)) + text_insert_msg(_("Error"), _("Unable to save configuration !")); + + gtk_widget_destroy(GTK_WIDGET(user_data)); +} + +void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *fs; + + fs = gtk_file_selection_new(_("Save file as...")); + g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), + "clicked", + G_CALLBACK(store_filename), (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->ok_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + g_signal_connect_swapped(GTK_OBJECT + (GTK_FILE_SELECTION(fs)->cancel_button), + "clicked", G_CALLBACK(gtk_widget_destroy), + (gpointer) fs); + gtk_widget_show(fs); +} + + +void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} + + +void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); + if (col) + gtk_tree_view_column_set_visible(col, show_name); +} + + +void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES); + if (col) + gtk_tree_view_column_set_visible(col, show_range); + +} + + +void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkTreeViewColumn *col; + + show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); + if (col) + gtk_tree_view_column_set_visible(col, show_value); +} + + +void +on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_NORMAL; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void +on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_ALL; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void +on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + opt_mode = OPT_PROMPT; + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); /* instead of update_tree to speed-up */ +} + + +void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *intro_text = _( + "Welcome to gkc, the GTK+ graphical configuration tool\n" + "For each option, a blank box indicates the feature is disabled, a\n" + "check indicates it is enabled, and a dot indicates that it is to\n" + "be compiled as a module. Clicking on the box will cycle through the three states.\n" + "\n" + "If you do not see an option (e.g., a device driver) that you\n" + "believe should be present, try turning on Show All Options\n" + "under the Options menu.\n" + "Although there is no cross reference yet to help you figure out\n" + "what other options must be enabled to support the option you\n" + "are interested in, you can still view the help of a grayed-out\n" + "option.\n" + "\n" + "Toggling Show Debug Info under the Options menu will show \n" + "the dependencies, which you can then match by examining other options."); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", intro_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *about_text = + _("gkc is copyright (c) 2002 Romain Lievin .\n" + "Based on the source code from Roman Zippel.\n"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", about_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +{ + GtkWidget *dialog; + const gchar *license_text = + _("gkc is released under the terms of the GNU GPL v2.\n" + "For more information, please see the source code or\n" + "visit http://www.fsf.org/licenses/licenses.html\n"); + + dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", license_text); + g_signal_connect_swapped(GTK_OBJECT(dialog), "response", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(dialog)); + gtk_widget_show_all(dialog); +} + + +void on_back_clicked(GtkButton * button, gpointer user_data) +{ + enum prop_type ptype; + + current = current->parent; + ptype = current->prompt ? current->prompt->type : P_UNKNOWN; + if (ptype != P_MENU) + current = current->parent; + display_tree_part(); + + if (current == &rootmenu) + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_load_clicked(GtkButton * button, gpointer user_data) +{ + on_load1_activate(NULL, user_data); +} + + +void on_single_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = SINGLE_VIEW; + gtk_widget_hide(tree1_w); + current = &rootmenu; + display_tree_part(); +} + + +void on_split_clicked(GtkButton * button, gpointer user_data) +{ + gint w, h; + view_mode = SPLIT_VIEW; + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + if (tree2) + gtk_tree_store_clear(tree2); + display_list(); + + /* Disable back btn, like in full mode. */ + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_full_clicked(GtkButton * button, gpointer user_data) +{ + view_mode = FULL_VIEW; + gtk_widget_hide(tree1_w); + if (tree2) + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); + gtk_widget_set_sensitive(back_btn, FALSE); +} + + +void on_collapse_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); +} + + +void on_expand_clicked(GtkButton * button, gpointer user_data) +{ + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + + +/* CTree Callbacks */ + +/* Change hex/int/string value in the cell */ +static void renderer_edited(GtkCellRendererText * cell, + const gchar * path_string, + const gchar * new_text, gpointer user_data) +{ + GtkTreePath *path = gtk_tree_path_new_from_string(path_string); + GtkTreeIter iter; + const char *old_def, *new_def; + struct menu *menu; + struct symbol *sym; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return; + + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + sym = menu->sym; + + gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + new_def = new_text; + + sym_set_string_value(sym, new_def); + + update_tree(&rootmenu, NULL); + + gtk_tree_path_free(path); +} + +/* Change the value of a symbol and update the tree */ +static void change_sym_value(struct menu *menu, gint col) +{ + struct symbol *sym = menu->sym; + tristate newval; + + if (!sym) + return; + + if (col == COL_NO) + newval = no; + else if (col == COL_MOD) + newval = mod; + else if (col == COL_YES) + newval = yes; + else + return; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (!sym_tristate_within_range(sym, newval)) + newval = yes; + sym_set_tristate_value(sym, newval); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll + break; + case S_INT: + case S_HEX: + case S_STRING: + default: + break; + } +} + +static void toggle_sym_value(struct menu *menu) +{ + if (!menu->sym) + return; + + sym_toggle_tristate_value(menu->sym); + if (view_mode == FULL_VIEW) + update_tree(&rootmenu, NULL); + else if (view_mode == SPLIT_VIEW) { + update_tree(browsed, NULL); + display_list(); + } + else if (view_mode == SINGLE_VIEW) + display_tree_part(); //fixme: keep exp/coll +} + +static gint column2index(GtkTreeViewColumn * column) +{ + gint i; + + for (i = 0; i < COL_NUMBER; i++) { + GtkTreeViewColumn *col; + + col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i); + if (col == column) + return i; + } + + return -1; +} + + +/* User click: update choice (full) or goes down (single) */ +gboolean +on_treeview2_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + +#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); +#else + gtk_tree_view_get_cursor(view, &path, &column); +#endif + if (path == NULL) + return FALSE; + + if (!gtk_tree_model_get_iter(model2, &iter, path)) + return FALSE; + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + col = column2index(column); + if (event->type == GDK_2BUTTON_PRESS) { + enum prop_type ptype; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + + if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + // goes down into menu + current = menu; + display_tree_part(); + gtk_widget_set_sensitive(back_btn, TRUE); + } else if (col == COL_OPTION) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } + } else { + if (col == COL_VALUE) { + toggle_sym_value(menu); + gtk_tree_view_expand_row(view, path, TRUE); + } else if (col == COL_NO || col == COL_MOD + || col == COL_YES) { + change_sym_value(menu, col); + gtk_tree_view_expand_row(view, path, TRUE); + } + } + + return FALSE; +} + +/* Key pressed: update choice */ +gboolean +on_treeview2_key_press_event(GtkWidget * widget, + GdkEventKey * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + gint col; + + gtk_tree_view_get_cursor(view, &path, &column); + if (path == NULL) + return FALSE; + + if (event->keyval == GDK_space) { + if (gtk_tree_view_row_expanded(view, path)) + gtk_tree_view_collapse_row(view, path); + else + gtk_tree_view_expand_row(view, path, FALSE); + return TRUE; + } + if (event->keyval == GDK_KP_Enter) { + } + if (widget == tree1_w) + return FALSE; + + gtk_tree_model_get_iter(model2, &iter, path); + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + + if (!strcasecmp(event->string, "n")) + col = COL_NO; + else if (!strcasecmp(event->string, "m")) + col = COL_MOD; + else if (!strcasecmp(event->string, "y")) + col = COL_YES; + else + col = -1; + change_sym_value(menu, col); + + return FALSE; +} + + +/* Row selection changed: update help */ +void +on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + struct menu *menu; + + selection = gtk_tree_view_get_selection(treeview); + if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { + gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + text_insert_help(menu); + } +} + + +/* User click: display sub-tree in the right frame. */ +gboolean +on_treeview1_button_press_event(GtkWidget * widget, + GdkEventButton * event, gpointer user_data) +{ + GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkTreeIter iter; + struct menu *menu; + + gint tx = (gint) event->x; + gint ty = (gint) event->y; + gint cx, cy; + + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, + &cy); + if (path == NULL) + return FALSE; + + gtk_tree_model_get_iter(model1, &iter, path); + gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + + if (event->type == GDK_2BUTTON_PRESS) { + toggle_sym_value(menu); + current = menu; + display_tree_part(); + } else { + browsed = menu; + display_tree_part(); + } + + gtk_widget_realize(tree2_w); + gtk_tree_view_set_cursor(view, path, NULL, FALSE); + gtk_widget_grab_focus(tree2_w); + + return FALSE; +} + + +/* Fill a row of strings */ +static gchar **fill_row(struct menu *menu) +{ + static gchar *row[COL_NUMBER]; + struct symbol *sym = menu->sym; + const char *def; + int stype; + tristate val; + enum prop_type ptype; + int i; + + for (i = COL_OPTION; i <= COL_COLOR; i++) + g_free(row[i]); + bzero(row, sizeof(row)); + + row[COL_OPTION] = + g_strdup_printf("%s %s", _(menu_get_prompt(menu)), + sym && !sym_has_value(sym) ? "(NEW)" : ""); + + if (opt_mode == OPT_ALL && !menu_is_visible(menu)) + row[COL_COLOR] = g_strdup("DarkGray"); + else if (opt_mode == OPT_PROMPT && + menu_has_prompt(menu) && !menu_is_visible(menu)) + row[COL_COLOR] = g_strdup("DarkGray"); + else + row[COL_COLOR] = g_strdup("Black"); + + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + switch (ptype) { + case P_MENU: + row[COL_PIXBUF] = (gchar *) xpm_menu; + if (view_mode == SINGLE_VIEW) + row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + case P_COMMENT: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + default: + row[COL_PIXBUF] = (gchar *) xpm_void; + row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + break; + } + + if (!sym) + return row; + row[COL_NAME] = g_strdup(sym->name); + + sym_calc_value(sym); + sym->flags &= ~SYMBOL_CHANGED; + + if (sym_is_choice(sym)) { // parse childs for getting final value + struct menu *child; + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) + && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + row[COL_VALUE] = + g_strdup(_(menu_get_prompt(def_menu))); + } + if (sym->flags & SYMBOL_CHOICEVAL) + row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); + + stype = sym_get_type(sym); + switch (stype) { + case S_BOOLEAN: + if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE) + row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); + if (sym_is_choice(sym)) + break; + /* fall through */ + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + row[COL_NO] = g_strdup("N"); + row[COL_VALUE] = g_strdup("N"); + row[COL_BTNACT] = GINT_TO_POINTER(FALSE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + case mod: + row[COL_MOD] = g_strdup("M"); + row[COL_VALUE] = g_strdup("M"); + row[COL_BTNINC] = GINT_TO_POINTER(TRUE); + break; + case yes: + row[COL_YES] = g_strdup("Y"); + row[COL_VALUE] = g_strdup("Y"); + row[COL_BTNACT] = GINT_TO_POINTER(TRUE); + row[COL_BTNINC] = GINT_TO_POINTER(FALSE); + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + row[COL_NO] = g_strdup("_"); + if (val != mod && sym_tristate_within_range(sym, mod)) + row[COL_MOD] = g_strdup("_"); + if (val != yes && sym_tristate_within_range(sym, yes)) + row[COL_YES] = g_strdup("_"); + break; + case S_INT: + case S_HEX: + case S_STRING: + def = sym_get_string_value(sym); + row[COL_VALUE] = g_strdup(def); + row[COL_EDIT] = GINT_TO_POINTER(TRUE); + row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); + break; + } + + return row; +} + + +/* Set the node content with a row of strings */ +static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) +{ + GdkColor color; + gboolean success; + GdkPixbuf *pix; + + pix = gdk_pixbuf_new_from_xpm_data((const char **) + row[COL_PIXBUF]); + + gdk_color_parse(row[COL_COLOR], &color); + gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, + FALSE, FALSE, &success); + + gtk_tree_store_set(tree, node, + COL_OPTION, row[COL_OPTION], + COL_NAME, row[COL_NAME], + COL_NO, row[COL_NO], + COL_MOD, row[COL_MOD], + COL_YES, row[COL_YES], + COL_VALUE, row[COL_VALUE], + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), + COL_PIXBUF, pix, + COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), + COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), + COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), + COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), + COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), + -1); + + g_object_unref(pix); +} + + +/* Add a node to the tree */ +static void place_node(struct menu *menu, char **row) +{ + GtkTreeIter *parent = parents[indent - 1]; + GtkTreeIter *node = parents[indent]; + + gtk_tree_store_append(tree, node, parent); + set_node(node, menu, row); +} + + +/* Find a node in the GTK+ tree */ +static GtkTreeIter found; + +/* + * Find a menu in the GtkTree starting at parent. + */ +GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent, + struct menu *tofind) +{ + GtkTreeIter iter; + GtkTreeIter *child = &iter; + gboolean valid; + GtkTreeIter *ret; + + valid = gtk_tree_model_iter_children(model2, child, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model2, child, 6, &menu, -1); + + if (menu == tofind) { + memcpy(&found, child, sizeof(GtkTreeIter)); + return &found; + } + + ret = gtktree_iter_find_node(child, tofind); + if (ret) + return ret; + + valid = gtk_tree_model_iter_next(model2, child); + } + + return NULL; +} + + +/* + * Update the tree by adding/removing entries + * Does not change other nodes + */ +static void update_tree(struct menu *src, GtkTreeIter * dst) +{ + struct menu *child1; + GtkTreeIter iter, tmp; + GtkTreeIter *child2 = &iter; + gboolean valid; + GtkTreeIter *sibling; + struct symbol *sym; + struct menu *menu1, *menu2; + + if (src == &rootmenu) + indent = 1; + + valid = gtk_tree_model_iter_children(model2, child2, dst); + for (child1 = src->list; child1; child1 = child1->next) { + + sym = child1->sym; + + reparse: + menu1 = child1; + if (valid) + gtk_tree_model_get(model2, child2, COL_MENU, + &menu2, -1); + else + menu2 = NULL; // force adding of a first child + +#ifdef DEBUG + printf("%*c%s | %s\n", indent, ' ', + menu1 ? menu_get_prompt(menu1) : "nil", + menu2 ? menu_get_prompt(menu2) : "nil"); +#endif + + if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) || + (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) || + (opt_mode == OPT_ALL && !menu_get_prompt(child1))) { + + /* remove node */ + if (gtktree_iter_find_node(dst, menu1) != NULL) { + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; /* next parent */ + else + goto reparse; /* next child */ + } else + continue; + } + + if (menu1 != menu2) { + if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node + if (!valid && !menu2) + sibling = NULL; + else + sibling = child2; + gtk_tree_store_insert_before(tree2, + child2, + dst, sibling); + set_node(child2, menu1, fill_row(menu1)); + if (menu2 == NULL) + valid = TRUE; + } else { // remove node + memcpy(&tmp, child2, sizeof(GtkTreeIter)); + valid = gtk_tree_model_iter_next(model2, + child2); + gtk_tree_store_remove(tree2, &tmp); + if (!valid) + return; // next parent + else + goto reparse; // next child + } + } else if (sym && (sym->flags & SYMBOL_CHANGED)) { + set_node(child2, menu1, fill_row(menu1)); + } + + indent++; + update_tree(child1, child2); + indent--; + + valid = gtk_tree_model_iter_next(model2, child2); + } +} + + +/* Display the whole tree (single/split/full view) */ +static void display_tree(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + enum prop_type ptype; + + if (menu == &rootmenu) { + indent = 1; + current = &rootmenu; + } + + for (child = menu->list; child; child = child->next) { + prop = child->prompt; + sym = child->sym; + ptype = prop ? prop->type : P_UNKNOWN; + + if (sym) + sym->flags &= ~SYMBOL_CHANGED; + + if ((view_mode == SPLIT_VIEW) + && !(child->flags & MENU_ROOT) && (tree == tree1)) + continue; + + if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT) + && (tree == tree2)) + continue; + + if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || + (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || + (opt_mode == OPT_ALL && menu_get_prompt(child))) + place_node(child, fill_row(child)); +#ifdef DEBUG + printf("%*c%s: ", indent, ' ', menu_get_prompt(child)); + printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : ""); + printf("%s", prop_get_type_name(ptype)); + printf(" | "); + if (sym) { + printf("%s", sym_type_name(sym->type)); + printf(" | "); + printf("%s", dbg_sym_flags(sym->flags)); + printf("\n"); + } else + printf("\n"); +#endif + if ((view_mode != FULL_VIEW) && (ptype == P_MENU) + && (tree == tree2)) + continue; +/* + if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW))*/ + + /* Change paned position if the view is not in 'split mode' */ + if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) { + gtk_paned_set_position(GTK_PANED(hpaned), 0); + } + + if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) + || (view_mode == FULL_VIEW) + || (view_mode == SPLIT_VIEW)) { + indent++; + display_tree(child); + indent--; + } + } +} + +/* Display a part of the tree starting at current node (single/split view) */ +static void display_tree_part(void) +{ + if (tree2) + gtk_tree_store_clear(tree2); + if (view_mode == SINGLE_VIEW) + display_tree(current); + else if (view_mode == SPLIT_VIEW) + display_tree(browsed); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); +} + +/* Display the list in the left frame (split view) */ +static void display_list(void) +{ + if (tree1) + gtk_tree_store_clear(tree1); + + tree = tree1; + display_tree(&rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); + tree = tree2; +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + + +/* Main */ +int main(int ac, char *av[]) +{ + const char *name; + char *env; + gchar *glade_file; + + bindtextdomain(PACKAGE, LOCALEDIR); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); + + /* GTK stuffs */ + gtk_set_locale(); + gtk_init(&ac, &av); + glade_init(); + + //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps"); + //add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps"); + + /* Determine GUI path */ + env = getenv(SRCTREE); + if (env) + glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); + else if (av[0][0] == '/') + glade_file = g_strconcat(av[0], ".glade", NULL); + else + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + + /* Conf stuffs */ + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 'a': + //showAll = 1; + break; + case 's': + conf_set_message_callback(NULL); + break; + case 'h': + case '?': + printf("%s [-s] \n", av[0]); + exit(0); + } + name = av[2]; + } else + name = av[1]; + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + + /* Load the interface and connect signals */ + init_main_window(glade_file); + init_tree_model(); + init_left_tree(); + init_right_tree(); + + switch (view_mode) { + case SINGLE_VIEW: + display_tree_part(); + break; + case SPLIT_VIEW: + display_list(); + break; + case FULL_VIEW: + display_tree(&rootmenu); + break; + } + + gtk_main(); + + return 0; +} + +static void conf_changed(void) +{ + bool changed = conf_get_changed(); + gtk_widget_set_sensitive(save_btn, changed); + gtk_widget_set_sensitive(save_menu_item, changed); +} diff --git a/tools/kconfig/gconf.glade b/tools/kconfig/gconf.glade new file mode 100644 index 0000000..aa483cb --- /dev/null +++ b/tools/kconfig/gconf.glade @@ -0,0 +1,661 @@ + + + + + + True + Gtk Kernel Configurator + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 640 + 480 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + + + + + + + True + False + 0 + + + + True + + + + True + _File + True + + + + + + + True + Load a config file + _Load + True + + + + + + True + gtk-open + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + Save the config in .config + _Save + True + + + + + + True + gtk-save + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + Save the config in a file + Save _as + True + + + + + True + gtk-save-as + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + + + + + + True + _Quit + True + + + + + + True + gtk-quit + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + + + + + True + _Options + True + + + + + + + True + Show name + Show _name + True + False + + + + + + + True + Show range (Y/M/N) + Show _range + True + False + + + + + + + True + Show value of the option + Show _data + True + False + + + + + + + True + + + + + + True + Show normal options + Show normal options + True + True + + + + + + + True + Show all options + Show all _options + True + False + set_option_mode1 + + + + + + + True + Show all options with prompts + Show all prompt options + True + False + set_option_mode1 + + + + + + + + + + + + True + _Help + True + + + + + + + True + _Introduction + True + + + + + + True + gtk-dialog-question + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + _About + True + + + + + + True + gtk-properties + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + _License + True + + + + + True + gtk-justify-fill + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + + + + 0 + False + False + + + + + + True + GTK_SHADOW_OUT + GTK_POS_LEFT + GTK_POS_TOP + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + True + + + + True + Goes up of one level (single view) + Back + True + gtk-undo + True + True + False + + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Load a config file + Load + True + gtk-open + True + True + False + + + + False + True + + + + + + True + Save a config file + Save + True + gtk-save + True + True + False + + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Single view + Single + True + gtk-missing-image + True + True + False + + + + False + True + + + + + + True + Split view + Split + True + gtk-missing-image + True + True + False + + + + False + True + + + + + + True + Full view + Full + True + gtk-missing-image + True + True + False + + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Collapse the whole tree in the right frame + Collapse + True + gtk-remove + True + True + False + + + + False + True + + + + + + True + Expand the whole tree in the right frame + Expand + True + gtk-add + True + True + False + + + + False + True + + + + + + + 0 + False + False + + + + + + 1 + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + False + + + + + + + + True + False + + + + + + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + True + False + False + False + + + + + + + + True + False + + + + + + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 0 + 0 + 0 + 0 + 0 + 0 + Sorry, no help available for this option yet. + + + + + True + True + + + + + True + True + + + + + 0 + True + True + + + + + + + diff --git a/tools/kconfig/images.c b/tools/kconfig/images.c new file mode 100644 index 0000000..d4f84bd --- /dev/null +++ b/tools/kconfig/images.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +static const char *xpm_load[] = { +"22 22 5 1", +". c None", +"# c #000000", +"c c #838100", +"a c #ffff00", +"b c #ffffff", +"......................", +"......................", +"......................", +"............####....#.", +"...........#....##.##.", +"..................###.", +".................####.", +".####...........#####.", +"#abab##########.......", +"#babababababab#.......", +"#ababababababa#.......", +"#babababababab#.......", +"#ababab###############", +"#babab##cccccccccccc##", +"#abab##cccccccccccc##.", +"#bab##cccccccccccc##..", +"#ab##cccccccccccc##...", +"#b##cccccccccccc##....", +"###cccccccccccc##.....", +"##cccccccccccc##......", +"###############.......", +"......................"}; + +static const char *xpm_save[] = { +"22 22 5 1", +". c None", +"# c #000000", +"a c #838100", +"b c #c5c2c5", +"c c #cdb6d5", +"......................", +".####################.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbbbb#bb#.", +".#aa#bbbbbbbbbcbb####.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbccbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aa#bbbbbbbbbbbb#aa#.", +".#aaa############aaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaaaaaaaaaaaaaaaaa#.", +".#aaa#############aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +".#aaa#########bbb#aa#.", +"..##################..", +"......................"}; + +static const char *xpm_back[] = { +"22 22 3 1", +". c None", +"# c #000083", +"a c #838183", +"......................", +"......................", +"......................", +"......................", +"......................", +"...........######a....", +"..#......##########...", +"..##...####......##a..", +"..###.###.........##..", +"..######..........##..", +"..#####...........##..", +"..######..........##..", +"..#######.........##..", +"..########.......##a..", +"...............a###...", +"...............###....", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................"}; + +static const char *xpm_tree_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......#...............", +"......########........", +"......................", +"......................"}; + +static const char *xpm_single_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"..........#...........", +"......................", +"......................"}; + +static const char *xpm_split_view[] = { +"22 22 2 1", +". c None", +"# c #000000", +"......................", +"......................", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......#......#........", +"......................", +"......................"}; + +static const char *xpm_symbol_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_mod[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_symbol_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . . ", +" . . . ", +" . .. . ", +" . . .. . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_choice_no[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_choice_yes[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .... ", +" .. .. ", +" . . ", +" . .. . ", +" . .... . ", +" . .... . ", +" . .. . ", +" . . ", +" .. .. ", +" .... ", +" "}; + +static const char *xpm_menu[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_menu_inv[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" .......... ", +" .. ...... ", +" .. .... ", +" .. .. ", +" .. .. ", +" .. .... ", +" .. ...... ", +" .......... ", +" .......... ", +" "}; + +static const char *xpm_menuback[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" .......... ", +" . . ", +" . .. . ", +" . .... . ", +" . ...... . ", +" . ...... . ", +" . .... . ", +" . .. . ", +" . . ", +" .......... ", +" "}; + +static const char *xpm_void[] = { +"12 12 2 1", +" c white", +". c black", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/tools/kconfig/kxgettext.c b/tools/kconfig/kxgettext.c new file mode 100644 index 0000000..2858738 --- /dev/null +++ b/tools/kconfig/kxgettext.c @@ -0,0 +1,235 @@ +/* + * Arnaldo Carvalho de Melo , 2005 + * + * Released under the terms of the GNU GPL v2.0 + */ + +#include +#include + +#include "lkc.h" + +static char *escape(const char* text, char *bf, int len) +{ + char *bfp = bf; + int multiline = strchr(text, '\n') != NULL; + int eol = 0; + int textlen = strlen(text); + + if ((textlen > 0) && (text[textlen-1] == '\n')) + eol = 1; + + *bfp++ = '"'; + --len; + + if (multiline) { + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 3; + } + + while (*text != '\0' && len > 1) { + if (*text == '"') + *bfp++ = '\\'; + else if (*text == '\n') { + *bfp++ = '\\'; + *bfp++ = 'n'; + *bfp++ = '"'; + *bfp++ = '\n'; + *bfp++ = '"'; + len -= 5; + ++text; + goto next; + } + else if (*text == '\\') { + *bfp++ = '\\'; + len--; + } + *bfp++ = *text++; +next: + --len; + } + + if (multiline && eol) + bfp -= 3; + + *bfp++ = '"'; + *bfp = '\0'; + + return bf; +} + +struct file_line { + struct file_line *next; + const char *file; + int lineno; +}; + +static struct file_line *file_line__new(const char *file, int lineno) +{ + struct file_line *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->file = file; + self->lineno = lineno; + self->next = NULL; +out: + return self; +} + +struct message { + const char *msg; + const char *option; + struct message *next; + struct file_line *files; +}; + +static struct message *message__list; + +static struct message *message__new(const char *msg, char *option, + const char *file, int lineno) +{ + struct message *self = malloc(sizeof(*self)); + + if (self == NULL) + goto out; + + self->files = file_line__new(file, lineno); + if (self->files == NULL) + goto out_fail; + + self->msg = strdup(msg); + if (self->msg == NULL) + goto out_fail_msg; + + self->option = option; + self->next = NULL; +out: + return self; +out_fail_msg: + free(self->files); +out_fail: + free(self); + self = NULL; + goto out; +} + +static struct message *mesage__find(const char *msg) +{ + struct message *m = message__list; + + while (m != NULL) { + if (strcmp(m->msg, msg) == 0) + break; + m = m->next; + } + + return m; +} + +static int message__add_file_line(struct message *self, const char *file, + int lineno) +{ + int rc = -1; + struct file_line *fl = file_line__new(file, lineno); + + if (fl == NULL) + goto out; + + fl->next = self->files; + self->files = fl; + rc = 0; +out: + return rc; +} + +static int message__add(const char *msg, char *option, const char *file, + int lineno) +{ + int rc = 0; + char bf[16384]; + char *escaped = escape(msg, bf, sizeof(bf)); + struct message *m = mesage__find(escaped); + + if (m != NULL) + rc = message__add_file_line(m, file, lineno); + else { + m = message__new(escaped, option, file, lineno); + + if (m != NULL) { + m->next = message__list; + message__list = m; + } else + rc = -1; + } + return rc; +} + +static void menu_build_message_list(struct menu *menu) +{ + struct menu *child; + + message__add(menu_get_prompt(menu), NULL, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + if (menu->sym != NULL && menu_has_help(menu)) + message__add(menu_get_help(menu), menu->sym->name, + menu->file == NULL ? "Root Menu" : menu->file->name, + menu->lineno); + + for (child = menu->list; child != NULL; child = child->next) + if (child->prompt != NULL) + menu_build_message_list(child); +} + +static void message__print_file_lineno(struct message *self) +{ + struct file_line *fl = self->files; + + putchar('\n'); + if (self->option != NULL) + printf("# %s:00000\n", self->option); + + printf("#: %s:%d", fl->file, fl->lineno); + fl = fl->next; + + while (fl != NULL) { + printf(", %s:%d", fl->file, fl->lineno); + fl = fl->next; + } + + putchar('\n'); +} + +static void message__print_gettext_msgid_msgstr(struct message *self) +{ + message__print_file_lineno(self); + + printf("msgid %s\n" + "msgstr \"\"\n", self->msg); +} + +static void menu__xgettext(void) +{ + struct message *m = message__list; + + while (m != NULL) { + /* skip empty lines ("") */ + if (strlen(m->msg) > sizeof("\"\"")) + message__print_gettext_msgid_msgstr(m); + m = m->next; + } +} + +int main(int ac, char **av) +{ + conf_parse(av[1]); + + menu_build_message_list(menu_get_root_menu(NULL)); + menu__xgettext(); + return 0; +} diff --git a/tools/kconfig/list.h b/tools/kconfig/list.h new file mode 100644 index 0000000..2cf23f0 --- /dev/null +++ b/tools/kconfig/list.h @@ -0,0 +1,131 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Copied from include/linux/... + */ + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +struct list_head { + struct list_head *next, *prev; +}; + + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_head within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *_new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = (struct list_head*)LIST_POISON1; + entry->prev = (struct list_head*)LIST_POISON2; +} +#endif diff --git a/tools/kconfig/lkc.h b/tools/kconfig/lkc.h new file mode 100644 index 0000000..91ca126 --- /dev/null +++ b/tools/kconfig/lkc.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#ifndef LKC_H +#define LKC_H + +#include "expr.h" + +#ifndef KBUILD_NO_NLS +# include +#else +static inline const char *gettext(const char *txt) { return txt; } +static inline void textdomain(const char *domainname) {} +static inline void bindtextdomain(const char *name, const char *dir) {} +static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; } +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lkc_proto.h" + +#define SRCTREE "srctree" + +#ifndef PACKAGE +#define PACKAGE "linux" +#endif + +#define LOCALEDIR "/usr/share/locale" + +#define _(text) gettext(text) +#define N_(text) (text) + +#ifndef CONFIG_ +#define CONFIG_ "CONFIG_" +#endif +static inline const char *CONFIG_prefix(void) +{ + return getenv( "CONFIG_" ) ?: CONFIG_; +} +#undef CONFIG_ +#define CONFIG_ CONFIG_prefix() + +#define TF_COMMAND 0x0001 +#define TF_PARAM 0x0002 +#define TF_OPTION 0x0004 + +enum conf_def_mode { + def_default, + def_yes, + def_mod, + def_no, + def_random +}; + +#define T_OPT_MODULES 1 +#define T_OPT_DEFCONFIG_LIST 2 +#define T_OPT_ENV 3 +#define T_OPT_ALLNOCONFIG_Y 4 + +struct kconf_id { + int name; + int token; + unsigned int flags; + enum symbol_type stype; +}; + +void zconfdump(FILE *out); +void zconf_starthelp(void); +FILE *zconf_fopen(const char *name); +void zconf_initscan(const char *name); +void zconf_nextfile(const char *name); +int zconf_lineno(void); +const char *zconf_curname(void); + +/* confdata.c */ +const char *conf_get_configname(void); +const char *conf_get_autoconfig_name(void); +char *conf_get_default_confname(void); +void sym_set_change_count(int count); +void sym_add_change_count(int count); +bool conf_set_all_new_symbols(enum conf_def_mode mode); +void set_all_choice_values(struct symbol *csym); + +/* confdata.c and expr.c */ +static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) +{ + assert(len != 0); + + if (fwrite(str, len, count, out) != count) + fprintf(stderr, "Error in writing or end of file.\n"); +} + +/* menu.c */ +void _menu_init(void); +void menu_warn(struct menu *menu, const char *fmt, ...); +struct menu *menu_add_menu(void); +void menu_end_menu(void); +void menu_add_entry(struct symbol *sym); +void menu_end_entry(void); +void menu_add_dep(struct expr *dep); +void menu_add_visibility(struct expr *dep); +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_add_option(int token, char *arg); +void menu_finalize(struct menu *parent); +void menu_set_type(int type); + +/* util.c */ +struct file *file_lookup(const char *name); +int file_write_dep(const char *name); +void *xmalloc(size_t size); +void *xcalloc(size_t nmemb, size_t size); + +struct gstr { + size_t len; + char *s; + /* + * when max_width is not zero long lines in string s (if any) get + * wrapped not to exceed the max_width value + */ + int max_width; +}; +struct gstr str_new(void); +void str_free(struct gstr *gs); +void str_append(struct gstr *gs, const char *s); +void str_printf(struct gstr *gs, const char *fmt, ...); +const char *str_get(struct gstr *gs); + +/* symbol.c */ +extern struct expr *sym_env_list; + +void sym_init(void); +void sym_clear_all_valid(void); +struct symbol *sym_choice_default(struct symbol *sym); +const char *sym_get_string_default(struct symbol *sym); +struct symbol *sym_check_deps(struct symbol *sym); +struct property *prop_alloc(enum prop_type type, struct symbol *sym); +struct symbol *prop_get_symbol(struct property *prop); +struct property *sym_get_env_prop(struct symbol *sym); + +static inline tristate sym_get_tristate_value(struct symbol *sym) +{ + return sym->curr.tri; +} + + +static inline struct symbol *sym_get_choice_value(struct symbol *sym) +{ + return (struct symbol *)sym->curr.val; +} + +static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval) +{ + return sym_set_tristate_value(chval, yes); +} + +static inline bool sym_is_choice(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICE ? true : false; +} + +static inline bool sym_is_choice_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_CHOICEVAL ? true : false; +} + +static inline bool sym_is_optional(struct symbol *sym) +{ + return sym->flags & SYMBOL_OPTIONAL ? true : false; +} + +static inline bool sym_has_value(struct symbol *sym) +{ + return sym->flags & SYMBOL_DEF_USER ? true : false; +} + +#ifdef __cplusplus +} +#endif + +#endif /* LKC_H */ diff --git a/tools/kconfig/lkc_proto.h b/tools/kconfig/lkc_proto.h new file mode 100644 index 0000000..d539871 --- /dev/null +++ b/tools/kconfig/lkc_proto.h @@ -0,0 +1,52 @@ +#include + +/* confdata.c */ +void conf_parse(const char *name); +int conf_read(const char *name); +int conf_read_simple(const char *name, int); +int conf_write_defconfig(const char *name); +int conf_write(const char *name); +int conf_write_autoconf(void); +bool conf_get_changed(void); +void conf_set_changed_callback(void (*fn)(void)); +void conf_set_message_callback(void (*fn)(const char *fmt, va_list ap)); + +/* menu.c */ +extern struct menu rootmenu; + +bool menu_is_empty(struct menu *menu); +bool menu_is_visible(struct menu *menu); +bool menu_has_prompt(struct menu *menu); +const char * menu_get_prompt(struct menu *menu); +struct menu * menu_get_root_menu(struct menu *menu); +struct menu * menu_get_parent_menu(struct menu *menu); +bool menu_has_help(struct menu *menu); +const char * menu_get_help(struct menu *menu); +struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); +void menu_get_ext_help(struct menu *menu, struct gstr *help); + +/* symbol.c */ +extern struct symbol * symbol_hash[SYMBOL_HASHSIZE]; + +struct symbol * sym_lookup(const char *name, int flags); +struct symbol * sym_find(const char *name); +const char * sym_expand_string_value(const char *in); +const char * sym_escape_string_value(const char *in); +struct symbol ** sym_re_search(const char *pattern); +const char * sym_type_name(enum symbol_type type); +void sym_calc_value(struct symbol *sym); +enum symbol_type sym_get_type(struct symbol *sym); +bool sym_tristate_within_range(struct symbol *sym,tristate tri); +bool sym_set_tristate_value(struct symbol *sym,tristate tri); +tristate sym_toggle_tristate_value(struct symbol *sym); +bool sym_string_valid(struct symbol *sym, const char *newval); +bool sym_string_within_range(struct symbol *sym, const char *str); +bool sym_set_string_value(struct symbol *sym, const char *newval); +bool sym_is_changable(struct symbol *sym); +struct property * sym_get_choice_prop(struct symbol *sym); +const char * sym_get_string_value(struct symbol *sym); + +const char * prop_get_type_name(enum prop_type type); + +/* expr.c */ +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); diff --git a/tools/kconfig/lxdialog/.gitignore b/tools/kconfig/lxdialog/.gitignore new file mode 100644 index 0000000..90b08ff --- /dev/null +++ b/tools/kconfig/lxdialog/.gitignore @@ -0,0 +1,4 @@ +# +# Generated files +# +lxdialog diff --git a/tools/kconfig/lxdialog/BIG.FAT.WARNING b/tools/kconfig/lxdialog/BIG.FAT.WARNING new file mode 100644 index 0000000..a8999d8 --- /dev/null +++ b/tools/kconfig/lxdialog/BIG.FAT.WARNING @@ -0,0 +1,4 @@ +This is NOT the official version of dialog. This version has been +significantly modified from the original. It is for use by the Linux +kernel configuration script. Please do not bother Savio Lam with +questions about this program. diff --git a/tools/kconfig/lxdialog/check-lxdialog.sh b/tools/kconfig/lxdialog/check-lxdialog.sh new file mode 100755 index 0000000..5075ebf --- /dev/null +++ b/tools/kconfig/lxdialog/check-lxdialog.sh @@ -0,0 +1,91 @@ +#!/bin/sh +# Check ncurses compatibility + +# What library to link +ldflags() +{ + pkg-config --libs ncursesw 2>/dev/null && exit + pkg-config --libs ncurses 2>/dev/null && exit + for ext in so a dll.a dylib ; do + for lib in ncursesw ncurses curses ; do + $cc -print-file-name=lib${lib}.${ext} | grep -q / + if [ $? -eq 0 ]; then + echo "-l${lib}" + exit + fi + done + done + exit 1 +} + +# Where is ncurses.h? +ccflags() +{ + if pkg-config --cflags ncursesw 2>/dev/null; then + echo '-DCURSES_LOC="" -DNCURSES_WIDECHAR=1' + elif pkg-config --cflags ncurses 2>/dev/null; then + echo '-DCURSES_LOC=""' + elif [ -f /usr/include/ncursesw/curses.h ]; then + echo '-I/usr/include/ncursesw -DCURSES_LOC=""' + echo ' -DNCURSES_WIDECHAR=1' + elif [ -f /usr/include/ncurses/ncurses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC=""' + elif [ -f /usr/include/ncurses/curses.h ]; then + echo '-I/usr/include/ncurses -DCURSES_LOC=""' + elif [ -f /usr/include/ncurses.h ]; then + echo '-DCURSES_LOC=""' + else + echo '-DCURSES_LOC=""' + fi +} + +# Temp file, try to clean up after us +tmp=.lxdialog.tmp +trap "rm -f $tmp" 0 1 2 3 15 + +# Check if we can link to ncurses +check() { + $cc -x c - -o $tmp 2>/dev/null <<'EOF' +#include CURSES_LOC +main() {} +EOF + if [ $? != 0 ]; then + echo " *** Unable to find the ncurses libraries or the" 1>&2 + echo " *** required header files." 1>&2 + echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2 + echo " *** " 1>&2 + echo " *** Install ncurses (ncurses-devel) and try again." 1>&2 + echo " *** " 1>&2 + exit 1 + fi +} + +usage() { + printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n" +} + +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +cc="" +case "$1" in + "-check") + shift + cc="$@" + check + ;; + "-ccflags") + ccflags + ;; + "-ldflags") + shift + cc="$@" + ldflags + ;; + "*") + usage + exit 1 + ;; +esac diff --git a/tools/kconfig/lxdialog/checklist.c b/tools/kconfig/lxdialog/checklist.c new file mode 100644 index 0000000..8d016fa --- /dev/null +++ b/tools/kconfig/lxdialog/checklist.c @@ -0,0 +1,332 @@ +/* + * checklist.c -- implements the checklist box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension + * Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static int list_width, check_x, item_x; + +/* + * Print list item + */ +static void print_item(WINDOW * win, int choice, int selected) +{ + int i; + char *list_item = malloc(list_width + 1); + + strncpy(list_item, item_str(), list_width - item_x); + list_item[list_width - item_x] = '\0'; + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, choice, 0); + for (i = 0; i < list_width; i++) + waddch(win, ' '); + + wmove(win, choice, check_x); + wattrset(win, selected ? dlg.check_selected.atr + : dlg.check.atr); + if (!item_is_tag(':')) + wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' '); + + wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr); + mvwaddch(win, choice, item_x, list_item[0]); + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + waddstr(win, list_item + 1); + if (selected) { + wmove(win, choice, check_x + 1); + wrefresh(win); + } + free(list_item); +} + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int choice, int item_no, int scroll, + int y, int x, int height) +{ + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + + if ((height < item_no) && (scroll + choice < item_no - 1)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } +} + +/* + * Display the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext("Select"), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with a list of options that can be turned on or off + * in the style of radiolist (only one option turned on at a time). + */ +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height) +{ + int i, x, y, box_x, box_y; + int key = 0, button = 0, choice = 0, scroll = 0, max_choice; + WINDOW *dialog, *list; + + /* which item to highlight */ + item_foreach() { + if (item_is_tag('X')) + choice = item_n(); + if (item_is_selected()) { + choice = item_n(); + break; + } + } + +do_resize: + if (getmaxy(stdscr) < (height + CHECKLIST_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + max_choice = MIN(list_height, item_count()); + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + list_width = width - 6; + box_y = height - list_height - 5; + box_x = (width - list_width) / 2 - 1; + + /* create new window for the list */ + list = subwin(dialog, list_height, list_width, y + box_y + 1, + x + box_x + 1); + + keypad(list, TRUE); + + /* draw a box around the list items */ + draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + /* Find length of longest item in order to center checklist */ + check_x = 0; + item_foreach() + check_x = MAX(check_x, strlen(item_str()) + 4); + check_x = MIN(check_x, list_width); + + check_x = (list_width - check_x) / 2; + item_x = check_x + 4; + + if (choice >= list_height) { + scroll = choice - list_height + 1; + choice -= scroll; + } + + /* Print the list */ + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + print_item(list, i, i == choice); + } + + print_arrows(dialog, choice, item_count(), scroll, + box_y, box_x + check_x + 5, list_height); + + print_buttons(dialog, height, width, 0); + + wnoutrefresh(dialog); + wnoutrefresh(list); + doupdate(); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + for (i = 0; i < max_choice; i++) { + item_set(i + scroll); + if (toupper(key) == toupper(item_str()[0])) + break; + } + + if (i < max_choice || key == KEY_UP || key == KEY_DOWN || + key == '+' || key == '-') { + if (key == KEY_UP || key == '-') { + if (!choice) { + if (!scroll) + continue; + /* Scroll list down */ + if (list_height > 1) { + /* De-highlight current first item */ + item_set(scroll); + print_item(list, 0, FALSE); + scrollok(list, TRUE); + wscrl(list, -1); + scrollok(list, FALSE); + } + scroll--; + item_set(scroll); + print_item(list, 0, TRUE); + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice - 1; + } else if (key == KEY_DOWN || key == '+') { + if (choice == max_choice - 1) { + if (scroll + choice >= item_count() - 1) + continue; + /* Scroll list up */ + if (list_height > 1) { + /* De-highlight current last item before scrolling up */ + item_set(scroll + max_choice - 1); + print_item(list, + max_choice - 1, + FALSE); + scrollok(list, TRUE); + wscrl(list, 1); + scrollok(list, FALSE); + } + scroll++; + item_set(scroll + max_choice - 1); + print_item(list, max_choice - 1, TRUE); + + print_arrows(dialog, choice, item_count(), + scroll, box_y, box_x + check_x + 5, list_height); + + wnoutrefresh(dialog); + wrefresh(list); + + continue; /* wait for another key press */ + } else + i = choice + 1; + } + if (i != choice) { + /* De-highlight current item */ + item_set(scroll + choice); + print_item(list, choice, FALSE); + /* Highlight new item */ + choice = i; + item_set(scroll + choice); + print_item(list, choice, TRUE); + wnoutrefresh(dialog); + wrefresh(list); + } + continue; /* wait for another key press */ + } + switch (key) { + case 'H': + case 'h': + case '?': + button = 1; + /* fall-through */ + case 'S': + case 's': + case ' ': + case '\n': + item_foreach() + item_set_selected(0); + item_set(scroll + choice); + item_set_selected(1); + delwin(list); + delwin(dialog); + return button; + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(list); + delwin(dialog); + on_key_resize(); + goto do_resize; + } + + /* Now, update everything... */ + doupdate(); + } + delwin(list); + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/tools/kconfig/lxdialog/dialog.h b/tools/kconfig/lxdialog/dialog.h new file mode 100644 index 0000000..fcffd5b --- /dev/null +++ b/tools/kconfig/lxdialog/dialog.h @@ -0,0 +1,257 @@ +/* + * dialog.h -- common declarations for all dialog modules + * + * AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef KBUILD_NO_NLS +# include +#else +# define gettext(Msgid) ((const char *) (Msgid)) +#endif + +#ifdef __sun__ +#define CURS_MACROS +#endif +#include CURSES_LOC + +/* + * Colors in ncurses 1.9.9e do not work properly since foreground and + * background colors are OR'd rather than separately masked. This version + * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible + * with standard curses. The simplest fix (to make this work with standard + * curses) uses the wbkgdset() function, not used in the original hack. + * Turn it off if we're building with 1.9.9e, since it just confuses things. + */ +#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE) +#define OLD_NCURSES 1 +#undef wbkgdset +#define wbkgdset(w,p) /*nothing */ +#else +#define OLD_NCURSES 0 +#endif + +#define TR(params) _tracef params + +#define KEY_ESC 27 +#define TAB 9 +#define MAX_LEN 2048 +#define BUF_SIZE (10*1024) +#define MIN(x,y) (x < y ? x : y) +#define MAX(x,y) (x > y ? x : y) + +#ifndef ACS_ULCORNER +#define ACS_ULCORNER '+' +#endif +#ifndef ACS_LLCORNER +#define ACS_LLCORNER '+' +#endif +#ifndef ACS_URCORNER +#define ACS_URCORNER '+' +#endif +#ifndef ACS_LRCORNER +#define ACS_LRCORNER '+' +#endif +#ifndef ACS_HLINE +#define ACS_HLINE '-' +#endif +#ifndef ACS_VLINE +#define ACS_VLINE '|' +#endif +#ifndef ACS_LTEE +#define ACS_LTEE '+' +#endif +#ifndef ACS_RTEE +#define ACS_RTEE '+' +#endif +#ifndef ACS_UARROW +#define ACS_UARROW '^' +#endif +#ifndef ACS_DARROW +#define ACS_DARROW 'v' +#endif + +/* error return codes */ +#define ERRDISPLAYTOOSMALL (KEY_MAX + 1) + +/* + * Color definitions + */ +struct dialog_color { + chtype atr; /* Color attribute */ + int fg; /* foreground */ + int bg; /* background */ + int hl; /* highlight this item */ +}; + +struct subtitle_list { + struct subtitle_list *next; + const char *text; +}; + +struct dialog_info { + const char *backtitle; + struct subtitle_list *subtitles; + struct dialog_color screen; + struct dialog_color shadow; + struct dialog_color dialog; + struct dialog_color title; + struct dialog_color border; + struct dialog_color button_active; + struct dialog_color button_inactive; + struct dialog_color button_key_active; + struct dialog_color button_key_inactive; + struct dialog_color button_label_active; + struct dialog_color button_label_inactive; + struct dialog_color inputbox; + struct dialog_color inputbox_border; + struct dialog_color searchbox; + struct dialog_color searchbox_title; + struct dialog_color searchbox_border; + struct dialog_color position_indicator; + struct dialog_color menubox; + struct dialog_color menubox_border; + struct dialog_color item; + struct dialog_color item_selected; + struct dialog_color tag; + struct dialog_color tag_selected; + struct dialog_color tag_key; + struct dialog_color tag_key_selected; + struct dialog_color check; + struct dialog_color check_selected; + struct dialog_color uarrow; + struct dialog_color darrow; +}; + +/* + * Global variables + */ +extern struct dialog_info dlg; +extern char dialog_input_result[]; +extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */ + +/* + * Function prototypes + */ + +/* item list as used by checklist and menubox */ +void item_reset(void); +void item_make(const char *fmt, ...); +void item_add_str(const char *fmt, ...); +void item_set_tag(char tag); +void item_set_data(void *p); +void item_set_selected(int val); +int item_activate_selected(void); +void *item_data(void); +char item_tag(void); + +/* item list manipulation for lxdialog use */ +#define MAXITEMSTR 200 +struct dialog_item { + char str[MAXITEMSTR]; /* prompt displayed */ + char tag; + void *data; /* pointer to menu item - used by menubox+checklist */ + int selected; /* Set to 1 by dialog_*() function if selected. */ +}; + +/* list of lialog_items */ +struct dialog_list { + struct dialog_item node; + struct dialog_list *next; +}; + +extern struct dialog_list *item_cur; +extern struct dialog_list item_nil; +extern struct dialog_list *item_head; + +int item_count(void); +void item_set(int n); +int item_n(void); +const char *item_str(void); +int item_is_selected(void); +int item_is_tag(char tag); +#define item_foreach() \ + for (item_cur = item_head ? item_head: item_cur; \ + item_cur && (item_cur != &item_nil); item_cur = item_cur->next) + +/* generic key handlers */ +int on_key_esc(WINDOW *win); +int on_key_resize(void); + +/* minimum (re)size values */ +#define CHECKLIST_HEIGTH_MIN 6 /* For dialog_checklist() */ +#define CHECKLIST_WIDTH_MIN 6 +#define INPUTBOX_HEIGTH_MIN 2 /* For dialog_inputbox() */ +#define INPUTBOX_WIDTH_MIN 2 +#define MENUBOX_HEIGTH_MIN 15 /* For dialog_menu() */ +#define MENUBOX_WIDTH_MIN 65 +#define TEXTBOX_HEIGTH_MIN 8 /* For dialog_textbox() */ +#define TEXTBOX_WIDTH_MIN 8 +#define YESNO_HEIGTH_MIN 4 /* For dialog_yesno() */ +#define YESNO_WIDTH_MIN 4 +#define WINDOW_HEIGTH_MIN 19 /* For init_dialog() */ +#define WINDOW_WIDTH_MIN 80 + +int init_dialog(const char *backtitle); +void set_dialog_backtitle(const char *backtitle); +void set_dialog_subtitles(struct subtitle_list *subtitles); +void end_dialog(int x, int y); +void attr_clear(WINDOW * win, int height, int width, chtype attr); +void dialog_clear(void); +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x); +void print_button(WINDOW * win, const char *label, int y, int x, int selected); +void print_title(WINDOW *dialog, const char *title, int width); +void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box, + chtype border); +void draw_shadow(WINDOW * win, int y, int x, int height, int width); + +int first_alpha(const char *string, const char *exempt); +int dialog_yesno(const char *title, const char *prompt, int height, int width); +int dialog_msgbox(const char *title, const char *prompt, int height, + int width, int pause); + + +typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void + *_data); +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data); +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll); +int dialog_checklist(const char *title, const char *prompt, int height, + int width, int list_height); +int dialog_inputbox(const char *title, const char *prompt, int height, + int width, const char *init); + +/* + * This is the base for fictitious keys, which activate + * the buttons. + * + * Mouse-generated keys are the following: + * -- the first 32 are used as numbers, in addition to '0'-'9' + * -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o') + * -- uppercase chars are used to invoke the button (M_EVENT + 'O') + */ +#define M_EVENT (KEY_MAX+1) diff --git a/tools/kconfig/lxdialog/inputbox.c b/tools/kconfig/lxdialog/inputbox.c new file mode 100644 index 0000000..d58de1d --- /dev/null +++ b/tools/kconfig/lxdialog/inputbox.c @@ -0,0 +1,301 @@ +/* + * inputbox.c -- implements the input box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +char dialog_input_result[MAX_LEN + 1]; + +/* + * Print the termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 11; + int y = height - 2; + + print_button(dialog, gettext(" Ok "), y, x, selected == 0); + print_button(dialog, gettext(" Help "), y, x + 14, selected == 1); + + wmove(dialog, y, x + 1 + 14 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box for inputing a string + */ +int dialog_inputbox(const char *title, const char *prompt, int height, int width, + const char *init) +{ + int i, x, y, box_y, box_x, box_width; + int input_x = 0, key = 0, button = -1; + int show_x, len, pos; + char *instr = dialog_input_result; + WINDOW *dialog; + + if (!init) + instr[0] = '\0'; + else + strcpy(instr, init); + +do_resize: + if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + /* Draw the input field box */ + box_width = width - 6; + getyx(dialog, y, x); + box_y = y + 2; + box_x = (width - box_width) / 2; + draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, + dlg.dialog.atr, dlg.border.atr); + + print_buttons(dialog, height, width, 0); + + /* Set up the initial value */ + wmove(dialog, box_y, box_x); + wattrset(dialog, dlg.inputbox.atr); + + len = strlen(instr); + pos = len; + + if (len >= box_width) { + show_x = len - box_width + 1; + input_x = box_width - 1; + for (i = 0; i < box_width - 1; i++) + waddch(dialog, instr[show_x + i]); + } else { + show_x = 0; + input_x = len; + waddstr(dialog, instr); + } + + wmove(dialog, box_y, box_x + input_x); + + wrefresh(dialog); + + while (key != KEY_ESC) { + key = wgetch(dialog); + + if (button == -1) { /* Input box selected */ + switch (key) { + case TAB: + case KEY_UP: + case KEY_DOWN: + break; + case KEY_BACKSPACE: + case 127: + if (pos) { + wattrset(dialog, dlg.inputbox.atr); + if (input_x == 0) { + show_x--; + } else + input_x--; + + if (pos < len) { + for (i = pos - 1; i < len; i++) { + instr[i] = instr[i+1]; + } + } + + pos--; + len--; + instr[len] = '\0'; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + wrefresh(dialog); + } + continue; + case KEY_LEFT: + if (pos > 0) { + if (input_x > 0) { + wmove(dialog, box_y, --input_x + box_x); + } else if (input_x == 0) { + show_x--; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, box_x); + } + pos--; + } + continue; + case KEY_RIGHT: + if (pos < len) { + if (input_x < box_width - 1) { + wmove(dialog, box_y, ++input_x + box_x); + } else if (input_x == box_width - 1) { + show_x++; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + } + pos++; + } + continue; + default: + if (key < 0x100 && isprint(key)) { + if (len < MAX_LEN) { + wattrset(dialog, dlg.inputbox.atr); + if (pos < len) { + for (i = len; i > pos; i--) + instr[i] = instr[i-1]; + instr[pos] = key; + } else { + instr[len] = key; + } + pos++; + len++; + instr[len] = '\0'; + + if (input_x == box_width - 1) { + show_x++; + } else { + input_x++; + } + + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + wrefresh(dialog); + } else + flash(); /* Alarm user about overflow */ + continue; + } + } + } + switch (key) { + case 'O': + case 'o': + delwin(dialog); + return 0; + case 'H': + case 'h': + delwin(dialog); + return 1; + case KEY_UP: + case KEY_LEFT: + switch (button) { + case -1: + button = 1; /* Indicates "Help" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 0: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + case 1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + } + break; + case TAB: + case KEY_DOWN: + case KEY_RIGHT: + switch (button) { + case -1: + button = 0; /* Indicates "OK" button is selected */ + print_buttons(dialog, height, width, 0); + break; + case 0: + button = 1; /* Indicates "Help" button is selected */ + print_buttons(dialog, height, width, 1); + break; + case 1: + button = -1; /* Indicates input box is selected */ + print_buttons(dialog, height, width, 0); + wmove(dialog, box_y, box_x + input_x); + wrefresh(dialog); + break; + } + break; + case ' ': + case '\n': + delwin(dialog); + return (button == -1 ? 0 : button); + case 'X': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return KEY_ESC; /* ESC pressed */ +} diff --git a/tools/kconfig/lxdialog/menubox.c b/tools/kconfig/lxdialog/menubox.c new file mode 100644 index 0000000..11ae9ad --- /dev/null +++ b/tools/kconfig/lxdialog/menubox.c @@ -0,0 +1,437 @@ +/* + * menubox.c -- implements the menu box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Changes by Clifford Wolf (god@clifford.at) + * + * [ 1998-06-13 ] + * + * *) A bugfix for the Page-Down problem + * + * *) Formerly when I used Page Down and Page Up, the cursor would be set + * to the first position in the menu box. Now lxdialog is a bit + * smarter and works more like other menu systems (just have a look at + * it). + * + * *) Formerly if I selected something my scrolling would be broken because + * lxdialog is re-invoked by the Menuconfig shell script, can't + * remember the last scrolling position, and just sets it so that the + * cursor is at the bottom of the box. Now it writes the temporary file + * lxdialog.scrltmp which contains this information. The file is + * deleted by lxdialog if the user leaves a submenu or enters a new + * one, but it would be nice if Menuconfig could make another "rm -f" + * just to be sure. Just try it out - you will recognise a difference! + * + * [ 1998-06-14 ] + * + * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files + * and menus change their size on the fly. + * + * *) If for some reason the last scrolling position is not saved by + * lxdialog, it sets the scrolling so that the selected item is in the + * middle of the menu box, not at the bottom. + * + * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net) + * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus. + * This fixes a bug in Menuconfig where using ' ' to descend into menus + * would leave mis-synchronized lxdialog.scrltmp files lying around, + * fscanf would read in 'scroll', and eventually that value would get used. + */ + +#include "dialog.h" + +static int menu_width, item_x; + +/* + * Print menu item + */ +static void do_print_item(WINDOW * win, const char *item, int line_y, + int selected, int hotkey) +{ + int j; + char *menu_item = malloc(menu_width + 1); + + strncpy(menu_item, item, menu_width - item_x); + menu_item[menu_width - item_x] = '\0'; + j = first_alpha(menu_item, "YyNnMmHh"); + + /* Clear 'residue' of last item */ + wattrset(win, dlg.menubox.atr); + wmove(win, line_y, 0); +#if OLD_NCURSES + { + int i; + for (i = 0; i < menu_width; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif + wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr); + mvwaddstr(win, line_y, item_x, menu_item); + if (hotkey) { + wattrset(win, selected ? dlg.tag_key_selected.atr + : dlg.tag_key.atr); + mvwaddch(win, line_y, item_x + j, menu_item[j]); + } + if (selected) { + wmove(win, line_y, item_x + 1); + } + free(menu_item); + wrefresh(win); +} + +#define print_item(index, choice, selected) \ +do { \ + item_set(index); \ + do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \ +} while (0) + +/* + * Print the scroll indicators. + */ +static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, + int height) +{ + int cur_y, cur_x; + + getyx(win, cur_y, cur_x); + + wmove(win, y, x); + + if (scroll > 0) { + wattrset(win, dlg.uarrow.atr); + waddch(win, ACS_UARROW); + waddstr(win, "(-)"); + } else { + wattrset(win, dlg.menubox.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + y = y + height + 1; + wmove(win, y, x); + wrefresh(win); + + if ((height < item_no) && (scroll + height < item_no)) { + wattrset(win, dlg.darrow.atr); + waddch(win, ACS_DARROW); + waddstr(win, "(+)"); + } else { + wattrset(win, dlg.menubox_border.atr); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + waddch(win, ACS_HLINE); + } + + wmove(win, cur_y, cur_x); + wrefresh(win); +} + +/* + * Display the termination buttons. + */ +static void print_buttons(WINDOW * win, int height, int width, int selected) +{ + int x = width / 2 - 28; + int y = height - 2; + + print_button(win, gettext("Select"), y, x, selected == 0); + print_button(win, gettext(" Exit "), y, x + 12, selected == 1); + print_button(win, gettext(" Help "), y, x + 24, selected == 2); + print_button(win, gettext(" Save "), y, x + 36, selected == 3); + print_button(win, gettext(" Load "), y, x + 48, selected == 4); + + wmove(win, y, x + 1 + 12 * selected); + wrefresh(win); +} + +/* scroll up n lines (n may be negative) */ +static void do_scroll(WINDOW *win, int *scroll, int n) +{ + /* Scroll menu up */ + scrollok(win, TRUE); + wscrl(win, n); + scrollok(win, FALSE); + *scroll = *scroll + n; + wrefresh(win); +} + +/* + * Display a menu for choosing among a number of options + */ +int dialog_menu(const char *title, const char *prompt, + const void *selected, int *s_scroll) +{ + int i, j, x, y, box_x, box_y; + int height, width, menu_height; + int key = 0, button = 0, scroll = 0, choice = 0; + int first_item = 0, max_choice; + WINDOW *dialog, *menu; + +do_resize: + height = getmaxy(stdscr); + width = getmaxx(stdscr); + if (height < MENUBOX_HEIGTH_MIN || width < MENUBOX_WIDTH_MIN) + return -ERRDISPLAYTOOSMALL; + + height -= 4; + width -= 5; + menu_height = height - 10; + + max_choice = MIN(menu_height, item_count()); + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + menu_width = width - 6; + box_y = height - menu_height - 5; + box_x = (width - menu_width) / 2 - 1; + + /* create new window for the menu */ + menu = subwin(dialog, menu_height, menu_width, + y + box_y + 1, x + box_x + 1); + keypad(menu, TRUE); + + /* draw a box around the menu items */ + draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2, + dlg.menubox_border.atr, dlg.menubox.atr); + + if (menu_width >= 80) + item_x = (menu_width - 70) / 2; + else + item_x = 4; + + /* Set choice to default item */ + item_foreach() + if (selected && (selected == item_data())) + choice = item_n(); + /* get the saved scroll info */ + scroll = *s_scroll; + if ((scroll <= choice) && (scroll + max_choice > choice) && + (scroll >= 0) && (scroll + max_choice <= item_count())) { + first_item = scroll; + choice = choice - scroll; + } else { + scroll = 0; + } + if ((choice >= max_choice)) { + if (choice >= item_count() - max_choice / 2) + scroll = first_item = item_count() - max_choice; + else + scroll = first_item = choice - max_choice / 2; + choice = choice - scroll; + } + + /* Print the menu */ + for (i = 0; i < max_choice; i++) { + print_item(first_item + i, i, i == choice); + } + + wnoutrefresh(menu); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + print_buttons(dialog, height, width, 0); + wmove(menu, choice, item_x + 1); + wrefresh(menu); + + while (key != KEY_ESC) { + key = wgetch(menu); + + if (key < 256 && isalpha(key)) + key = tolower(key); + + if (strchr("ynmh", key)) + i = max_choice; + else { + for (i = choice + 1; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + if (i == max_choice) + for (i = 0; i < max_choice; i++) { + item_set(scroll + i); + j = first_alpha(item_str(), "YyNnMmHh"); + if (key == tolower(item_str()[j])) + break; + } + } + + if (item_count() != 0 && + (i < max_choice || + key == KEY_UP || key == KEY_DOWN || + key == '-' || key == '+' || + key == KEY_PPAGE || key == KEY_NPAGE)) { + /* Remove highligt of current item */ + print_item(scroll + choice, choice, FALSE); + + if (key == KEY_UP || key == '-') { + if (choice < 2 && scroll) { + /* Scroll menu down */ + do_scroll(menu, &scroll, -1); + + print_item(scroll, 0, FALSE); + } else + choice = MAX(choice - 1, 0); + + } else if (key == KEY_DOWN || key == '+') { + print_item(scroll+choice, choice, FALSE); + + if ((choice > max_choice - 3) && + (scroll + max_choice < item_count())) { + /* Scroll menu up */ + do_scroll(menu, &scroll, 1); + + print_item(scroll+max_choice - 1, + max_choice - 1, FALSE); + } else + choice = MIN(choice + 1, max_choice - 1); + + } else if (key == KEY_PPAGE) { + scrollok(menu, TRUE); + for (i = 0; (i < max_choice); i++) { + if (scroll > 0) { + do_scroll(menu, &scroll, -1); + print_item(scroll, 0, FALSE); + } else { + if (choice > 0) + choice--; + } + } + + } else if (key == KEY_NPAGE) { + for (i = 0; (i < max_choice); i++) { + if (scroll + max_choice < item_count()) { + do_scroll(menu, &scroll, 1); + print_item(scroll+max_choice-1, + max_choice - 1, FALSE); + } else { + if (choice + 1 < max_choice) + choice++; + } + } + } else + choice = i; + + print_item(scroll + choice, choice, TRUE); + + print_arrows(dialog, item_count(), scroll, + box_y, box_x + item_x + 1, menu_height); + + wnoutrefresh(dialog); + wrefresh(menu); + + continue; /* wait for another key press */ + } + + switch (key) { + case KEY_LEFT: + case TAB: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) + ? 4 : (button > 4 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(menu); + break; + case ' ': + case 's': + case 'y': + case 'n': + case 'm': + case '/': + case 'h': + case '?': + case 'z': + case '\n': + /* save scroll info */ + *s_scroll = scroll; + delwin(menu); + delwin(dialog); + item_set(scroll + choice); + item_set_selected(1); + switch (key) { + case 'h': + case '?': + return 2; + case 's': + case 'y': + return 5; + case 'n': + return 6; + case 'm': + return 7; + case ' ': + return 8; + case '/': + return 9; + case 'z': + return 10; + case '\n': + return button; + } + return 0; + case 'e': + case 'x': + key = KEY_ESC; + break; + case KEY_ESC: + key = on_key_esc(menu); + break; + case KEY_RESIZE: + on_key_resize(); + delwin(menu); + delwin(dialog); + goto do_resize; + } + } + delwin(menu); + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/tools/kconfig/lxdialog/textbox.c b/tools/kconfig/lxdialog/textbox.c new file mode 100644 index 0000000..1773319 --- /dev/null +++ b/tools/kconfig/lxdialog/textbox.c @@ -0,0 +1,408 @@ +/* + * textbox.c -- implements the text box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +static void back_lines(int n); +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data); +static void print_line(WINDOW *win, int row, int width); +static char *get_line(void); +static void print_position(WINDOW * win); + +static int hscroll; +static int begin_reached, end_reached, page_length; +static char *buf; +static char *page; + +/* + * refresh window content + */ +static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, + int cur_y, int cur_x, update_text_fn update_text, + void *data) +{ + print_page(box, boxh, boxw, update_text, data); + print_position(dialog); + wmove(dialog, cur_y, cur_x); /* Restore cursor position */ + wrefresh(dialog); +} + + +/* + * Display text from a file in a dialog box. + * + * keys is a null-terminated array + * update_text() may not add or remove any '\n' or '\0' in tbuf + */ +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data) +{ + int i, x, y, cur_x, cur_y, key = 0; + int height, width, boxh, boxw; + WINDOW *dialog, *box; + bool done = false; + + begin_reached = 1; + end_reached = 0; + page_length = 0; + hscroll = 0; + buf = tbuf; + page = buf; /* page is pointer to start of page to be displayed */ + + if (_vscroll && *_vscroll) { + begin_reached = 0; + + for (i = 0; i < *_vscroll; i++) + get_line(); + } + if (_hscroll) + hscroll = *_hscroll; + +do_resize: + getmaxyx(stdscr, height, width); + if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN) + return -ERRDISPLAYTOOSMALL; + if (initial_height != 0) + height = initial_height; + else + if (height > 4) + height -= 4; + else + height = 0; + if (initial_width != 0) + width = initial_width; + else + if (width > 5) + width -= 5; + else + width = 0; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + /* Create window for box region, used for scrolling text */ + boxh = height - 4; + boxw = width - 2; + box = subwin(dialog, boxh, boxw, y + 1, x + 1); + wattrset(box, dlg.dialog.atr); + wbkgdset(box, dlg.dialog.atr & A_COLOR); + + keypad(box, TRUE); + + /* register the new window, along with its borders */ + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + wbkgdset(dialog, dlg.dialog.atr & A_COLOR); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE); + wnoutrefresh(dialog); + getyx(dialog, cur_y, cur_x); /* Save cursor position */ + + /* Print first page of text */ + attr_clear(box, boxh, boxw, dlg.dialog.atr); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text, + data); + + while (!done) { + key = wgetch(dialog); + switch (key) { + case 'E': /* Exit */ + case 'e': + case 'X': + case 'x': + case 'q': + case '\n': + done = true; + break; + case 'g': /* First page */ + case KEY_HOME: + if (!begin_reached) { + begin_reached = 1; + page = buf; + refresh_text_box(dialog, box, boxh, boxw, + cur_y, cur_x, update_text, + data); + } + break; + case 'G': /* Last page */ + case KEY_END: + + end_reached = 1; + /* point to last char in buf */ + page = buf + strlen(buf); + back_lines(boxh); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'K': /* Previous line */ + case 'k': + case KEY_UP: + if (begin_reached) + break; + + back_lines(page_length + 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'B': /* Previous page */ + case 'b': + case 'u': + case KEY_PPAGE: + if (begin_reached) + break; + back_lines(page_length + boxh); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'J': /* Next line */ + case 'j': + case KEY_DOWN: + if (end_reached) + break; + + back_lines(page_length - 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case KEY_NPAGE: /* Next page */ + case ' ': + case 'd': + if (end_reached) + break; + + begin_reached = 0; + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case '0': /* Beginning of line */ + case 'H': /* Scroll left */ + case 'h': + case KEY_LEFT: + if (hscroll <= 0) + break; + + if (key == '0') + hscroll = 0; + else + hscroll--; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case 'L': /* Scroll right */ + case 'l': + case KEY_RIGHT: + if (hscroll >= MAX_LEN) + break; + hscroll++; + /* Reprint current page to scroll horizontally */ + back_lines(page_length); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); + break; + case KEY_ESC: + if (on_key_esc(dialog) == KEY_ESC) + done = true; + break; + case KEY_RESIZE: + back_lines(height); + delwin(box); + delwin(dialog); + on_key_resize(); + goto do_resize; + default: + for (i = 0; keys[i]; i++) { + if (key == keys[i]) { + done = true; + break; + } + } + } + } + delwin(box); + delwin(dialog); + if (_vscroll) { + const char *s; + + s = buf; + *_vscroll = 0; + back_lines(page_length); + while (s < page && (s = strchr(s, '\n'))) { + (*_vscroll)++; + s++; + } + } + if (_hscroll) + *_hscroll = hscroll; + return key; +} + +/* + * Go back 'n' lines in text. Called by dialog_textbox(). + * 'page' will be updated to point to the desired line in 'buf'. + */ +static void back_lines(int n) +{ + int i; + + begin_reached = 0; + /* Go back 'n' lines */ + for (i = 0; i < n; i++) { + if (*page == '\0') { + if (end_reached) { + end_reached = 0; + continue; + } + } + if (page == buf) { + begin_reached = 1; + return; + } + page--; + do { + if (page == buf) { + begin_reached = 1; + return; + } + page--; + } while (*page != '\n'); + page++; + } +} + +/* + * Print a new page of text. + */ +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data) +{ + int i, passed_end = 0; + + if (update_text) { + char *end; + + for (i = 0; i < height; i++) + get_line(); + end = page; + back_lines(height); + update_text(buf, page - buf, end - buf, data); + } + + page_length = 0; + for (i = 0; i < height; i++) { + print_line(win, i, width); + if (!passed_end) + page_length++; + if (end_reached && !passed_end) + passed_end = 1; + } + wnoutrefresh(win); +} + +/* + * Print a new line of text. + */ +static void print_line(WINDOW * win, int row, int width) +{ + char *line; + + line = get_line(); + line += MIN(strlen(line), hscroll); /* Scroll horizontally */ + wmove(win, row, 0); /* move cursor to correct line */ + waddch(win, ' '); + waddnstr(win, line, MIN(strlen(line), width - 2)); + + /* Clear 'residue' of previous line */ +#if OLD_NCURSES + { + int x = getcurx(win); + int i; + for (i = 0; i < width - x; i++) + waddch(win, ' '); + } +#else + wclrtoeol(win); +#endif +} + +/* + * Return current line of text. Called by dialog_textbox() and print_line(). + * 'page' should point to start of current line before calling, and will be + * updated to point to start of next line. + */ +static char *get_line(void) +{ + int i = 0; + static char line[MAX_LEN + 1]; + + end_reached = 0; + while (*page != '\n') { + if (*page == '\0') { + end_reached = 1; + break; + } else if (i < MAX_LEN) + line[i++] = *(page++); + else { + /* Truncate lines longer than MAX_LEN characters */ + if (i == MAX_LEN) + line[i++] = '\0'; + page++; + } + } + if (i <= MAX_LEN) + line[i] = '\0'; + if (!end_reached) + page++; /* move past '\n' */ + + return line; +} + +/* + * Print current position + */ +static void print_position(WINDOW * win) +{ + int percent; + + wattrset(win, dlg.position_indicator.atr); + wbkgdset(win, dlg.position_indicator.atr & A_COLOR); + percent = (page - buf) * 100 / strlen(buf); + wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); + wprintw(win, "(%3d%%)", percent); +} diff --git a/tools/kconfig/lxdialog/util.c b/tools/kconfig/lxdialog/util.c new file mode 100644 index 0000000..f7abdeb --- /dev/null +++ b/tools/kconfig/lxdialog/util.c @@ -0,0 +1,713 @@ +/* + * util.c + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "dialog.h" + +/* Needed in signal handler in mconf.c */ +int saved_x, saved_y; + +struct dialog_info dlg; + +static void set_mono_theme(void) +{ + dlg.screen.atr = A_NORMAL; + dlg.shadow.atr = A_NORMAL; + dlg.dialog.atr = A_NORMAL; + dlg.title.atr = A_BOLD; + dlg.border.atr = A_NORMAL; + dlg.button_active.atr = A_REVERSE; + dlg.button_inactive.atr = A_DIM; + dlg.button_key_active.atr = A_REVERSE; + dlg.button_key_inactive.atr = A_BOLD; + dlg.button_label_active.atr = A_REVERSE; + dlg.button_label_inactive.atr = A_NORMAL; + dlg.inputbox.atr = A_NORMAL; + dlg.inputbox_border.atr = A_NORMAL; + dlg.searchbox.atr = A_NORMAL; + dlg.searchbox_title.atr = A_BOLD; + dlg.searchbox_border.atr = A_NORMAL; + dlg.position_indicator.atr = A_BOLD; + dlg.menubox.atr = A_NORMAL; + dlg.menubox_border.atr = A_NORMAL; + dlg.item.atr = A_NORMAL; + dlg.item_selected.atr = A_REVERSE; + dlg.tag.atr = A_BOLD; + dlg.tag_selected.atr = A_REVERSE; + dlg.tag_key.atr = A_BOLD; + dlg.tag_key_selected.atr = A_REVERSE; + dlg.check.atr = A_BOLD; + dlg.check_selected.atr = A_REVERSE; + dlg.uarrow.atr = A_BOLD; + dlg.darrow.atr = A_BOLD; +} + +#define DLG_COLOR(dialog, f, b, h) \ +do { \ + dlg.dialog.fg = (f); \ + dlg.dialog.bg = (b); \ + dlg.dialog.hl = (h); \ +} while (0) + +static void set_classic_theme(void) +{ + DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true); + DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false); + DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true); + DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(inputbox_border, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(searchbox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true); + DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false); + DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true); + DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true); +} + +static void set_blackbg_theme(void) +{ + DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true); + DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false); + DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false); + DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false); + DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(inputbox_border, COLOR_YELLOW, COLOR_BLACK, false); + + DLG_COLOR(searchbox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(searchbox_title, COLOR_YELLOW, COLOR_BLACK, true); + DLG_COLOR(searchbox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false); + + DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true); + + DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false); + DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false); + + DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true); + DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false); + DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true); + + DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false); + DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false); +} + +static void set_bluetitle_theme(void) +{ + set_classic_theme(); + DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true); + DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true); + DLG_COLOR(searchbox_title, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true); + DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true); + +} + +/* + * Select color theme + */ +static int set_theme(const char *theme) +{ + int use_color = 1; + if (!theme) + set_bluetitle_theme(); + else if (strcmp(theme, "classic") == 0) + set_classic_theme(); + else if (strcmp(theme, "bluetitle") == 0) + set_bluetitle_theme(); + else if (strcmp(theme, "blackbg") == 0) + set_blackbg_theme(); + else if (strcmp(theme, "mono") == 0) + use_color = 0; + + return use_color; +} + +static void init_one_color(struct dialog_color *color) +{ + static int pair = 0; + + pair++; + init_pair(pair, color->fg, color->bg); + if (color->hl) + color->atr = A_BOLD | COLOR_PAIR(pair); + else + color->atr = COLOR_PAIR(pair); +} + +static void init_dialog_colors(void) +{ + init_one_color(&dlg.screen); + init_one_color(&dlg.shadow); + init_one_color(&dlg.dialog); + init_one_color(&dlg.title); + init_one_color(&dlg.border); + init_one_color(&dlg.button_active); + init_one_color(&dlg.button_inactive); + init_one_color(&dlg.button_key_active); + init_one_color(&dlg.button_key_inactive); + init_one_color(&dlg.button_label_active); + init_one_color(&dlg.button_label_inactive); + init_one_color(&dlg.inputbox); + init_one_color(&dlg.inputbox_border); + init_one_color(&dlg.searchbox); + init_one_color(&dlg.searchbox_title); + init_one_color(&dlg.searchbox_border); + init_one_color(&dlg.position_indicator); + init_one_color(&dlg.menubox); + init_one_color(&dlg.menubox_border); + init_one_color(&dlg.item); + init_one_color(&dlg.item_selected); + init_one_color(&dlg.tag); + init_one_color(&dlg.tag_selected); + init_one_color(&dlg.tag_key); + init_one_color(&dlg.tag_key_selected); + init_one_color(&dlg.check); + init_one_color(&dlg.check_selected); + init_one_color(&dlg.uarrow); + init_one_color(&dlg.darrow); +} + +/* + * Setup for color display + */ +static void color_setup(const char *theme) +{ + int use_color; + + use_color = set_theme(theme); + if (use_color && has_colors()) { + start_color(); + init_dialog_colors(); + } else + set_mono_theme(); +} + +/* + * Set window to attribute 'attr' + */ +void attr_clear(WINDOW * win, int height, int width, chtype attr) +{ + int i, j; + + wattrset(win, attr); + for (i = 0; i < height; i++) { + wmove(win, i, 0); + for (j = 0; j < width; j++) + waddch(win, ' '); + } + touchwin(win); +} + +void dialog_clear(void) +{ + int lines, columns; + + lines = getmaxy(stdscr); + columns = getmaxx(stdscr); + + attr_clear(stdscr, lines, columns, dlg.screen.atr); + /* Display background title if it exists ... - SLH */ + if (dlg.backtitle != NULL) { + int i, len = 0, skip = 0; + struct subtitle_list *pos; + + wattrset(stdscr, dlg.screen.atr); + mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle); + + for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { + /* 3 is for the arrow and spaces */ + len += strlen(pos->text) + 3; + } + + wmove(stdscr, 1, 1); + if (len > columns - 2) { + const char *ellipsis = "[...] "; + waddstr(stdscr, ellipsis); + skip = len - (columns - 2 - strlen(ellipsis)); + } + + for (pos = dlg.subtitles; pos != NULL; pos = pos->next) { + if (skip == 0) + waddch(stdscr, ACS_RARROW); + else + skip--; + + if (skip == 0) + waddch(stdscr, ' '); + else + skip--; + + if (skip < strlen(pos->text)) { + waddstr(stdscr, pos->text + skip); + skip = 0; + } else + skip -= strlen(pos->text); + + if (skip == 0) + waddch(stdscr, ' '); + else + skip--; + } + + for (i = len + 1; i < columns - 1; i++) + waddch(stdscr, ACS_HLINE); + } + wnoutrefresh(stdscr); +} + +/* + * Do some initialization for dialog + */ +int init_dialog(const char *backtitle) +{ + int height, width; + + initscr(); /* Init curses */ + + /* Get current cursor position for signal handler in mconf.c */ + getyx(stdscr, saved_y, saved_x); + + getmaxyx(stdscr, height, width); + if (height < WINDOW_HEIGTH_MIN || width < WINDOW_WIDTH_MIN) { + endwin(); + return -ERRDISPLAYTOOSMALL; + } + + dlg.backtitle = backtitle; + color_setup(getenv("MENUCONFIG_COLOR")); + + keypad(stdscr, TRUE); + cbreak(); + noecho(); + dialog_clear(); + + return 0; +} + +void set_dialog_backtitle(const char *backtitle) +{ + dlg.backtitle = backtitle; +} + +void set_dialog_subtitles(struct subtitle_list *subtitles) +{ + dlg.subtitles = subtitles; +} + +/* + * End using dialog functions. + */ +void end_dialog(int x, int y) +{ + /* move cursor back to original position */ + move(y, x); + refresh(); + endwin(); +} + +/* Print the title of the dialog. Center the title and truncate + * tile if wider than dialog (- 2 chars). + **/ +void print_title(WINDOW *dialog, const char *title, int width) +{ + if (title) { + int tlen = MIN(width - 2, strlen(title)); + wattrset(dialog, dlg.title.atr); + mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' '); + mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen); + waddch(dialog, ' '); + } +} + +/* + * Print a string of text in a window, automatically wrap around to the + * next line if the string is too long to fit on one line. Newline + * characters '\n' are propperly processed. We start on a new line + * if there is no room for at least 4 nonblanks following a double-space. + */ +void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) +{ + int newl, cur_x, cur_y; + int prompt_len, room, wlen; + char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0; + + strcpy(tempstr, prompt); + + prompt_len = strlen(tempstr); + + if (prompt_len <= width - x * 2) { /* If prompt is short */ + wmove(win, y, (width - prompt_len) / 2); + waddstr(win, tempstr); + } else { + cur_x = x; + cur_y = y; + newl = 1; + word = tempstr; + while (word && *word) { + sp = strpbrk(word, "\n "); + if (sp && *sp == '\n') + newline_separator = sp; + + if (sp) + *sp++ = 0; + + /* Wrap to next line if either the word does not fit, + or it is the first word of a new sentence, and it is + short, and the next word does not fit. */ + room = width - cur_x; + wlen = strlen(word); + if (wlen > room || + (newl && wlen < 4 && sp + && wlen + 1 + strlen(sp) > room + && (!(sp2 = strpbrk(sp, "\n ")) + || wlen + 1 + (sp2 - sp) > room))) { + cur_y++; + cur_x = x; + } + wmove(win, cur_y, cur_x); + waddstr(win, word); + getyx(win, cur_y, cur_x); + + /* Move to the next line if the word separator was a newline */ + if (newline_separator) { + cur_y++; + cur_x = x; + newline_separator = 0; + } else + cur_x++; + + if (sp && *sp == ' ') { + cur_x++; /* double space */ + while (*++sp == ' ') ; + newl = 1; + } else + newl = 0; + word = sp; + } + } +} + +/* + * Print a button + */ +void print_button(WINDOW * win, const char *label, int y, int x, int selected) +{ + int i, temp; + + wmove(win, y, x); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, "<"); + temp = strspn(label, " "); + label += temp; + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + for (i = 0; i < temp; i++) + waddch(win, ' '); + wattrset(win, selected ? dlg.button_key_active.atr + : dlg.button_key_inactive.atr); + waddch(win, label[0]); + wattrset(win, selected ? dlg.button_label_active.atr + : dlg.button_label_inactive.atr); + waddstr(win, (char *)label + 1); + wattrset(win, selected ? dlg.button_active.atr + : dlg.button_inactive.atr); + waddstr(win, ">"); + wmove(win, y, x + temp + 1); +} + +/* + * Draw a rectangular box with line drawing characters + */ +void +draw_box(WINDOW * win, int y, int x, int height, int width, + chtype box, chtype border) +{ + int i, j; + + wattrset(win, 0); + for (i = 0; i < height; i++) { + wmove(win, y + i, x); + for (j = 0; j < width; j++) + if (!i && !j) + waddch(win, border | ACS_ULCORNER); + else if (i == height - 1 && !j) + waddch(win, border | ACS_LLCORNER); + else if (!i && j == width - 1) + waddch(win, box | ACS_URCORNER); + else if (i == height - 1 && j == width - 1) + waddch(win, box | ACS_LRCORNER); + else if (!i) + waddch(win, border | ACS_HLINE); + else if (i == height - 1) + waddch(win, box | ACS_HLINE); + else if (!j) + waddch(win, border | ACS_VLINE); + else if (j == width - 1) + waddch(win, box | ACS_VLINE); + else + waddch(win, box | ' '); + } +} + +/* + * Draw shadows along the right and bottom edge to give a more 3D look + * to the boxes + */ +void draw_shadow(WINDOW * win, int y, int x, int height, int width) +{ + int i; + + if (has_colors()) { /* Whether terminal supports color? */ + wattrset(win, dlg.shadow.atr); + wmove(win, y + height, x + 2); + for (i = 0; i < width; i++) + waddch(win, winch(win) & A_CHARTEXT); + for (i = y + 1; i < y + height + 1; i++) { + wmove(win, i, x + width); + waddch(win, winch(win) & A_CHARTEXT); + waddch(win, winch(win) & A_CHARTEXT); + } + wnoutrefresh(win); + } +} + +/* + * Return the position of the first alphabetic character in a string. + */ +int first_alpha(const char *string, const char *exempt) +{ + int i, in_paren = 0, c; + + for (i = 0; i < strlen(string); i++) { + c = tolower(string[i]); + + if (strchr("<[(", c)) + ++in_paren; + if (strchr(">])", c) && in_paren > 0) + --in_paren; + + if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0) + return i; + } + + return 0; +} + +/* + * ncurses uses ESC to detect escaped char sequences. This resutl in + * a small timeout before ESC is actually delivered to the application. + * lxdialog suggest which is correctly translated to two + * times esc. But then we need to ignore the second esc to avoid stepping + * out one menu too much. Filter away all escaped key sequences since + * keypad(FALSE) turn off ncurses support for escape sequences - and thats + * needed to make notimeout() do as expected. + */ +int on_key_esc(WINDOW *win) +{ + int key; + int key2; + int key3; + + nodelay(win, TRUE); + keypad(win, FALSE); + key = wgetch(win); + key2 = wgetch(win); + do { + key3 = wgetch(win); + } while (key3 != ERR); + nodelay(win, FALSE); + keypad(win, TRUE); + if (key == KEY_ESC && key2 == ERR) + return KEY_ESC; + else if (key != ERR && key != KEY_ESC && key2 == ERR) + ungetch(key); + + return -1; +} + +/* redraw screen in new size */ +int on_key_resize(void) +{ + dialog_clear(); + return KEY_RESIZE; +} + +struct dialog_list *item_cur; +struct dialog_list item_nil; +struct dialog_list *item_head; + +void item_reset(void) +{ + struct dialog_list *p, *next; + + for (p = item_head; p; p = next) { + next = p->next; + free(p); + } + item_head = NULL; + item_cur = &item_nil; +} + +void item_make(const char *fmt, ...) +{ + va_list ap; + struct dialog_list *p = malloc(sizeof(*p)); + + if (item_head) + item_cur->next = p; + else + item_head = p; + item_cur = p; + memset(p, 0, sizeof(*p)); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap); + va_end(ap); +} + +void item_add_str(const char *fmt, ...) +{ + va_list ap; + size_t avail; + + avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str); + + va_start(ap, fmt); + vsnprintf(item_cur->node.str + strlen(item_cur->node.str), + avail, fmt, ap); + item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0'; + va_end(ap); +} + +void item_set_tag(char tag) +{ + item_cur->node.tag = tag; +} +void item_set_data(void *ptr) +{ + item_cur->node.data = ptr; +} + +void item_set_selected(int val) +{ + item_cur->node.selected = val; +} + +int item_activate_selected(void) +{ + item_foreach() + if (item_is_selected()) + return 1; + return 0; +} + +void *item_data(void) +{ + return item_cur->node.data; +} + +char item_tag(void) +{ + return item_cur->node.tag; +} + +int item_count(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) + n++; + return n; +} + +void item_set(int n) +{ + int i = 0; + item_foreach() + if (i++ == n) + return; +} + +int item_n(void) +{ + int n = 0; + struct dialog_list *p; + + for (p = item_head; p; p = p->next) { + if (p == item_cur) + return n; + n++; + } + return 0; +} + +const char *item_str(void) +{ + return item_cur->node.str; +} + +int item_is_selected(void) +{ + return (item_cur->node.selected != 0); +} + +int item_is_tag(char tag) +{ + return (item_cur->node.tag == tag); +} diff --git a/tools/kconfig/lxdialog/yesno.c b/tools/kconfig/lxdialog/yesno.c new file mode 100644 index 0000000..676fb2f --- /dev/null +++ b/tools/kconfig/lxdialog/yesno.c @@ -0,0 +1,114 @@ +/* + * yesno.c -- implements the yes/no box + * + * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) + * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dialog.h" + +/* + * Display termination buttons + */ +static void print_buttons(WINDOW * dialog, int height, int width, int selected) +{ + int x = width / 2 - 10; + int y = height - 2; + + print_button(dialog, gettext(" Yes "), y, x, selected == 0); + print_button(dialog, gettext(" No "), y, x + 13, selected == 1); + + wmove(dialog, y, x + 1 + 13 * selected); + wrefresh(dialog); +} + +/* + * Display a dialog box with two buttons - Yes and No + */ +int dialog_yesno(const char *title, const char *prompt, int height, int width) +{ + int i, x, y, key = 0, button = 0; + WINDOW *dialog; + +do_resize: + if (getmaxy(stdscr) < (height + YESNO_HEIGTH_MIN)) + return -ERRDISPLAYTOOSMALL; + if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN)) + return -ERRDISPLAYTOOSMALL; + + /* center dialog box on screen */ + x = (getmaxx(stdscr) - width) / 2; + y = (getmaxy(stdscr) - height) / 2; + + draw_shadow(stdscr, y, x, height, width); + + dialog = newwin(height, width, y, x); + keypad(dialog, TRUE); + + draw_box(dialog, 0, 0, height, width, + dlg.dialog.atr, dlg.border.atr); + wattrset(dialog, dlg.border.atr); + mvwaddch(dialog, height - 3, 0, ACS_LTEE); + for (i = 0; i < width - 2; i++) + waddch(dialog, ACS_HLINE); + wattrset(dialog, dlg.dialog.atr); + waddch(dialog, ACS_RTEE); + + print_title(dialog, title, width); + + wattrset(dialog, dlg.dialog.atr); + print_autowrap(dialog, prompt, width - 2, 1, 3); + + print_buttons(dialog, height, width, 0); + + while (key != KEY_ESC) { + key = wgetch(dialog); + switch (key) { + case 'Y': + case 'y': + delwin(dialog); + return 0; + case 'N': + case 'n': + delwin(dialog); + return 1; + + case TAB: + case KEY_LEFT: + case KEY_RIGHT: + button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button); + + print_buttons(dialog, height, width, button); + wrefresh(dialog); + break; + case ' ': + case '\n': + delwin(dialog); + return button; + case KEY_ESC: + key = on_key_esc(dialog); + break; + case KEY_RESIZE: + delwin(dialog); + on_key_resize(); + goto do_resize; + } + } + + delwin(dialog); + return key; /* ESC pressed */ +} diff --git a/tools/kconfig/mconf.c b/tools/kconfig/mconf.c new file mode 100644 index 0000000..315ce2c --- /dev/null +++ b/tools/kconfig/mconf.c @@ -0,0 +1,1047 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + * + * Introduced single menu mode (show all sub-menus in one large tree). + * 2002-11-06 Petr Baudis + * + * i18n, 2005, Arnaldo Carvalho de Melo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lkc.h" +#include "lxdialog/dialog.h" + +static const char mconf_readme[] = N_( +"Overview\n" +"--------\n" +"This interface lets you select features and parameters for the build.\n" +"Features can either be built-in, modularized, or ignored. Parameters\n" +"must be entered in as decimal or hexadecimal numbers or text.\n" +"\n" +"Menu items beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized (selected by other feature)\n" +" - - are selected by other feature,\n" +"while *, M or whitespace inside braces means to build in, build as\n" +"a module or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the cursor\n" +"keys and press to build it in, to make it a module or\n" +" to remove it. You may also press the to cycle\n" +"through the available options (i.e. Y->N->M->Y).\n" +"\n" +"Some additional keyboard hints:\n" +"\n" +"Menus\n" +"----------\n" +"o Use the Up/Down arrow keys (cursor keys) to highlight the item you\n" +" wish to change or the submenu you wish to select and press .\n" +" Submenus are designated by \"--->\", empty ones by \"----\".\n" +"\n" +" Shortcut: Press the option's highlighted letter (hotkey).\n" +" Pressing a hotkey more than once will sequence\n" +" through all visible items which use that hotkey.\n" +"\n" +" You may also use the and keys to scroll\n" +" unseen options into view.\n" +"\n" +"o To exit a menu use the cursor keys to highlight the button\n" +" and press .\n" +"\n" +" Shortcut: Press or or if there is no hotkey\n" +" using those letters. You may press a single , but\n" +" there is a delayed response which you may find annoying.\n" +"\n" +" Also, the and cursor keys will cycle between and\n" +" \n" +"\n" +"\n" +"Data Entry\n" +"-----------\n" +"o Enter the requested information and press \n" +" If you are entering hexadecimal values, it is not necessary to\n" +" add the '0x' prefix to the entry.\n" +"\n" +"o For help, use the or cursor keys to highlight the help option\n" +" and press . You can try as well.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"--------\n" +"o Use the cursor keys to scroll up/down/left/right. The VI editor\n" +" keys h,j,k,l function here as do , , and for\n" +" those who are familiar with less and lynx.\n" +"\n" +"o Press , , , or to exit.\n" +"\n" +"\n" +"Alternate Configuration Files\n" +"-----------------------------\n" +"Menuconfig supports the use of alternate configuration files for\n" +"those who, for various reasons, find it necessary to switch\n" +"between different configurations.\n" +"\n" +"The button will let you save the current configuration to\n" +"a file of your choosing. Use the button to load a previously\n" +"saved alternate configuration.\n" +"\n" +"Even if you don't use alternate configuration files, but you find\n" +"during a Menuconfig session that you have completely messed up your\n" +"settings, you may use the button to restore your previously\n" +"saved settings from \".config\" without restarting Menuconfig.\n" +"\n" +"Other information\n" +"-----------------\n" +"If you use Menuconfig in an XTERM window, make sure you have your\n" +"$TERM variable set to point to an xterm definition which supports\n" +"color. Otherwise, Menuconfig will look rather bad. Menuconfig will\n" +"not display correctly in an RXVT window because rxvt displays only one\n" +"intensity of color, bright.\n" +"\n" +"Menuconfig will display larger menus on screens or xterms which are\n" +"set to display more than the standard 25 row by 80 column geometry.\n" +"In order for this to work, the \"stty size\" command must be able to\n" +"display the screen's current row and column geometry. I STRONGLY\n" +"RECOMMEND that you make sure you do NOT have the shell variables\n" +"LINES and COLUMNS exported into your environment. Some distributions\n" +"export those variables via /etc/profile. Some ncurses programs can\n" +"become confused when those variables (LINES & COLUMNS) don't reflect\n" +"the true screen size.\n" +"\n" +"Optional personality available\n" +"------------------------------\n" +"If you prefer to have all of the options listed in a single menu,\n" +"rather than the default multimenu hierarchy, run the menuconfig with\n" +"MENUCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make MENUCONFIG_MODE=single_menu menuconfig\n" +"\n" +" will then unroll the appropriate category, or enfold it if it\n" +"is already unrolled.\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive\n" +"(especially with a larger number of unrolled categories) than the\n" +"default mode.\n" +"\n" +"Different color themes available\n" +"--------------------------------\n" +"It is possible to select different color themes using the variable\n" +"MENUCONFIG_COLOR. To select a theme use:\n" +"\n" +"make MENUCONFIG_COLOR= menuconfig\n" +"\n" +"Available themes are\n" +" mono => selects colors suitable for monochrome displays\n" +" blackbg => selects a color scheme with black background\n" +" classic => theme with blue background. The classic look\n" +" bluetitle => an LCD friendly version of classic. (default)\n" +"\n"), +menu_instructions[] = N_( + "Arrow keys navigate the menu. " + " selects submenus ---> (or empty submenus ----). " + "Highlighted letters are hotkeys. " + "Pressing includes, excludes, modularizes features. " + "Press to exit, for Help, for Search. " + "Legend: [*] built-in [ ] excluded module < > module capable"), +radiolist_instructions[] = N_( + "Use the arrow keys to navigate this window or " + "press the hotkey of the item you wish to select " + "followed by the . " + "Press for additional information about this option."), +inputbox_instructions_int[] = N_( + "Please enter a decimal value. " + "Fractions will not be accepted. " + "Use the key to move from the input field to the buttons below it."), +inputbox_instructions_hex[] = N_( + "Please enter a hexadecimal value. " + "Use the key to move from the input field to the buttons below it."), +inputbox_instructions_string[] = N_( + "Please enter a string value. " + "Use the key to move from the input field to the buttons below it."), +setmod_text[] = N_( + "This feature depends on another which has been configured as a module.\n" + "As a result, this feature will be built as a module."), +load_config_text[] = N_( + "Enter the name of the configuration file you wish to load. " + "Accept the name shown to restore the configuration you " + "last retrieved. Leave blank to abort."), +load_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep several different\n" + "configurations available on a single machine.\n" + "\n" + "If you have saved a previous configuration in a file other than the\n" + "default one, entering its name here will allow you to modify that\n" + "configuration.\n" + "\n" + "If you are uncertain, then you have probably never used alternate\n" + "configuration files. You should therefore leave this blank to abort.\n"), +save_config_text[] = N_( + "Enter a filename to which this configuration should be saved " + "as an alternate. Leave blank to abort."), +save_config_help[] = N_( + "\n" + "For various reasons, one may wish to keep different configurations\n" + "available on a single machine.\n" + "\n" + "Entering a file name here will allow you to later retrieve, modify\n" + "and use the current configuration as an alternate to whatever\n" + "configuration options you have selected at that time.\n" + "\n" + "If you are uncertain what all this means then you should probably\n" + "leave this blank.\n"), +search_help[] = N_( + "\n" + "Search for symbols and display their relations.\n" + "Regular expressions are allowed.\n" + "Example: search for \"^FOO\"\n" + "Result:\n" + "-----------------------------------------------------------------\n" + "Symbol: FOO [=m]\n" + "Type : tristate\n" + "Prompt: Foo bus is used to drive the bar HW\n" + " Location:\n" + " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" + " -> PCI support (PCI [=y])\n" + "(1) -> PCI access mode ( [=y])\n" + " Defined at drivers/pci/Kconfig:47\n" + " Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" + " Selects: LIBCRC32\n" + " Selected by: BAR [=n]\n" + "-----------------------------------------------------------------\n" + "o The line 'Type:' shows the type of the configuration option for\n" + " this symbol (boolean, tristate, string, ...)\n" + "o The line 'Prompt:' shows the text used in the menu structure for\n" + " this symbol\n" + "o The 'Defined at' line tells at what file / line number the symbol\n" + " is defined\n" + "o The 'Depends on:' line tells what symbols need to be defined for\n" + " this symbol to be visible in the menu (selectable)\n" + "o The 'Location:' lines tells where in the menu structure this symbol\n" + " is located\n" + " A location followed by a [=y] indicates that this is a\n" + " selectable menu item - and the current value is displayed inside\n" + " brackets.\n" + " Press the key in the (#) prefix to jump directly to that\n" + " location. You will be returned to the current search results\n" + " after exiting this new menu.\n" + "o The 'Selects:' line tells what symbols will be automatically\n" + " selected if this symbol is selected (y or m)\n" + "o The 'Selected by' line tells what symbol has selected this symbol\n" + "\n" + "Only relevant lines are shown.\n" + "\n\n" + "Search examples:\n" + "Examples: USB => find all symbols containing USB\n" + " ^USB => find all symbols starting with USB\n" + " USB$ => find all symbols ending with USB\n" + "\n"); + +static int indent; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; +static int show_all_options; +static int save_and_exit; +static int silent; + +static void conf(struct menu *menu, struct menu *active_menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static int show_textbox_ext(const char *title, char *text, int r, int c, + int *keys, int *vscroll, int *hscroll, + update_text_fn update_text, void *data); +static void show_textbox(const char *title, const char *text, int r, int c); +static void show_helptext(const char *title, const char *text); +static void show_help(struct menu *menu); + +static char filename[PATH_MAX+1]; +static void set_config_filename(const char *config_filename) +{ + static char menu_backtitle[PATH_MAX+128]; + int size; + + size = snprintf(menu_backtitle, sizeof(menu_backtitle), + "%s - %s", config_filename, rootmenu.prompt->text); + if (size >= sizeof(menu_backtitle)) + menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; + set_dialog_backtitle(menu_backtitle); + + size = snprintf(filename, sizeof(filename), "%s", config_filename); + if (size >= sizeof(filename)) + filename[sizeof(filename)-1] = '\0'; +} + +struct subtitle_part { + struct list_head entries; + const char *text; +}; +static LIST_HEAD(trail); + +static struct subtitle_list *subtitles; +static void set_subtitle(void) +{ + struct subtitle_part *sp; + struct subtitle_list *pos, *tmp; + + for (pos = subtitles; pos != NULL; pos = tmp) { + tmp = pos->next; + free(pos); + } + + subtitles = NULL; + list_for_each_entry(sp, &trail, entries) { + if (sp->text) { + if (pos) { + pos->next = xcalloc(1, sizeof(*pos)); + pos = pos->next; + } else { + subtitles = pos = xcalloc(1, sizeof(*pos)); + } + pos->text = sp->text; + } + } + + set_dialog_subtitles(subtitles); +} + +static void reset_subtitle(void) +{ + struct subtitle_list *pos, *tmp; + + for (pos = subtitles; pos != NULL; pos = tmp) { + tmp = pos->next; + free(pos); + } + subtitles = NULL; + set_dialog_subtitles(subtitles); +} + +struct search_data { + struct list_head *head; + struct menu **targets; + int *keys; +}; + +static void update_text(char *buf, size_t start, size_t end, void *_data) +{ + struct search_data *data = _data; + struct jump_key *pos; + int k = 0; + + list_for_each_entry(pos, data->head, entries) { + if (pos->offset >= start && pos->offset < end) { + char header[4]; + + if (k < JUMP_NB) { + int key = '0' + (pos->index % JUMP_NB) + 1; + + sprintf(header, "(%c)", key); + data->keys[k] = key; + data->targets[k] = pos->target; + k++; + } else { + sprintf(header, " "); + } + + memcpy(buf + pos->offset, header, sizeof(header) - 1); + } + } + data->keys[k] = 0; +} + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + struct gstr title; + char *dialog_input; + int dres, vscroll = 0, hscroll = 0; + bool again; + struct gstr sttext; + struct subtitle_part stpart; + + title = str_new(); + str_printf( &title, _("Enter (sub)string or regexp to search for " + "(with or without \"%s\")"), CONFIG_); + +again: + dialog_clear(); + dres = dialog_inputbox(_("Search Configuration Parameter"), + str_get(&title), + 10, 75, ""); + switch (dres) { + case 0: + break; + case 1: + show_helptext(_("Search Configuration"), search_help); + goto again; + default: + str_free(&title); + return; + } + + /* strip the prefix if necessary */ + dialog_input = dialog_input_result; + if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) + dialog_input += strlen(CONFIG_); + + sttext = str_new(); + str_printf(&sttext, "Search (%s)", dialog_input_result); + stpart.text = str_get(&sttext); + list_add_tail(&stpart.entries, &trail); + + sym_arr = sym_re_search(dialog_input); + do { + LIST_HEAD(head); + struct menu *targets[JUMP_NB]; + int keys[JUMP_NB + 1], i; + struct search_data data = { + .head = &head, + .targets = targets, + .keys = keys, + }; + struct jump_key *pos, *tmp; + + res = get_relations_str(sym_arr, &head); + set_subtitle(); + dres = show_textbox_ext(_("Search Results"), (char *) + str_get(&res), 0, 0, keys, &vscroll, + &hscroll, &update_text, (void *) + &data); + again = false; + for (i = 0; i < JUMP_NB && keys[i]; i++) + if (dres == keys[i]) { + conf(targets[i]->parent, targets[i]); + again = true; + } + str_free(&res); + list_for_each_entry_safe(pos, tmp, &head, entries) + free(pos); + } while (again); + free(sym_arr); + str_free(&title); + list_del(trail.prev); + str_free(&sttext); +} + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + bool visible; + + /* + * note: menu_is_visible() has side effect that it will + * recalc the value of the symbol. + */ + visible = menu_is_visible(menu); + if (show_all_options && !menu_has_prompt(menu)) + return; + else if (!show_all_options && !visible) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + switch (prop->type) { + case P_MENU: + child_count++; + prompt = _(prompt); + if (single_menu_mode) { + item_make("%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else + item_make(" %*c%s %s", + indent + 1, ' ', prompt, + menu_is_empty(menu) ? "----" : "--->"); + item_set_tag('m'); + item_set_data(menu); + if (single_menu_mode && menu->data) + goto conf_childs; + return; + case P_COMMENT: + if (prompt) { + child_count++; + item_make(" %*c*** %s ***", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + break; + default: + if (prompt) { + child_count++; + item_make("---%*c%s", indent + 1, ' ', _(prompt)); + item_set_tag(':'); + item_set_data(menu); + } + } + } else + doint = 0; + goto conf_childs; + } + + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + switch (type) { + case S_BOOLEAN: + item_make("[%c]", val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + item_make("<%c>", ch); + break; + } + item_set_tag('t'); + item_set_data(menu); + } else { + item_make(" "); + item_set_tag(def_menu ? 't' : ':'); + item_set_data(menu); + } + + item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + if (val == yes) { + if (def_menu) { + item_add_str(" (%s)", _(menu_get_prompt(def_menu))); + item_add_str(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu))); + item_set_tag(':'); + item_set_data(menu); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + item_make(" "); + item_set_tag(':'); + item_set_data(menu); + } else { + switch (type) { + case S_BOOLEAN: + if (sym_is_changable(sym)) + item_make("[%c]", val == no ? ' ' : '*'); + else + item_make("-%c-", val == no ? ' ' : '*'); + item_set_tag('t'); + item_set_data(menu); + break; + case S_TRISTATE: + switch (val) { + case yes: ch = '*'; break; + case mod: ch = 'M'; break; + default: ch = ' '; break; + } + if (sym_is_changable(sym)) { + if (sym->rev_dep.tri == mod) + item_make("{%c}", ch); + else + item_make("<%c>", ch); + } else + item_make("-%c-", ch); + item_set_tag('t'); + item_set_data(menu); + break; + default: + tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */ + item_make("(%s)", sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + item_set_tag('s'); + item_set_data(menu); + goto conf_childs; + } + } + item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + if (menu->prompt->type == P_MENU) { + item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void conf(struct menu *menu, struct menu *active_menu) +{ + struct menu *submenu; + const char *prompt = menu_get_prompt(menu); + struct subtitle_part stpart; + struct symbol *sym; + int res; + int s_scroll = 0; + + if (menu != &rootmenu) + stpart.text = menu_get_prompt(menu); + else + stpart.text = NULL; + list_add_tail(&stpart.entries, &trail); + + while (1) { + item_reset(); + current_menu = menu; + build_conf(menu); + if (!child_count) + break; + set_subtitle(); + dialog_clear(); + res = dialog_menu(prompt ? _(prompt) : _("Main Menu"), + _(menu_instructions), + active_menu, &s_scroll); + if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL) + break; + if (item_count() != 0) { + if (!item_activate_selected()) + continue; + if (!item_tag()) + continue; + } + submenu = item_data(); + active_menu = item_data(); + if (submenu) + sym = submenu->sym; + else + sym = NULL; + + switch (res) { + case 0: + switch (item_tag()) { + case 'm': + if (single_menu_mode) + submenu->data = (void *) (long) !submenu->data; + else + conf(submenu, NULL); + break; + case 't': + if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt->type == P_MENU) + conf(submenu, NULL); + break; + case 's': + conf_string(submenu); + break; + } + break; + case 2: + if (sym) + show_help(submenu); + else { + reset_subtitle(); + show_helptext(_("README"), _(mconf_readme)); + } + break; + case 3: + reset_subtitle(); + conf_save(); + break; + case 4: + reset_subtitle(); + conf_load(); + break; + case 5: + if (item_is_tag('t')) { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + show_textbox(NULL, setmod_text, 6, 74); + } + break; + case 6: + if (item_is_tag('t')) + sym_set_tristate_value(sym, no); + break; + case 7: + if (item_is_tag('t')) + sym_set_tristate_value(sym, mod); + break; + case 8: + if (item_is_tag('t')) + sym_toggle_tristate_value(sym); + else if (item_is_tag('m')) + conf(submenu, NULL); + break; + case 9: + search_conf(); + break; + case 10: + show_all_options = !show_all_options; + break; + } + } + + list_del(trail.prev); +} + +static int show_textbox_ext(const char *title, char *text, int r, int c, int + *keys, int *vscroll, int *hscroll, update_text_fn + update_text, void *data) +{ + dialog_clear(); + return dialog_textbox(title, text, r, c, keys, vscroll, hscroll, + update_text, data); +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL, + NULL, NULL); +} + +static void show_helptext(const char *title, const char *text) +{ + show_textbox(title, text, 0, 0); +} + +static void conf_message_callback(const char *fmt, va_list ap) +{ + char buf[PATH_MAX+1]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + if (save_and_exit) { + if (!silent) + printf("%s", buf); + } else { + show_textbox(NULL, buf, 6, 60); + } +} + +static void show_help(struct menu *menu) +{ + struct gstr help = str_new(); + + help.max_width = getmaxx(stdscr) - 10; + menu_get_ext_help(menu, &help); + + show_helptext(_(menu_get_prompt(menu)), str_get(&help)); + str_free(&help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = _(menu_get_prompt(menu)); + struct menu *child; + struct symbol *active; + + active = sym_get_choice_value(menu->sym); + while (1) { + int res; + int selected; + item_reset(); + + current_menu = menu; + for (child = menu->list; child; child = child->next) { + if (!menu_is_visible(child)) + continue; + if (child->sym) + item_make("%s", _(menu_get_prompt(child))); + else { + item_make("*** %s ***", _(menu_get_prompt(child))); + item_set_tag(':'); + } + item_set_data(child); + if (child->sym == active) + item_set_selected(1); + if (child->sym == sym_get_choice_value(menu->sym)) + item_set_tag('X'); + } + dialog_clear(); + res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"), + _(radiolist_instructions), + MENUBOX_HEIGTH_MIN, + MENUBOX_WIDTH_MIN, + CHECKLIST_HEIGTH_MIN); + selected = item_activate_selected(); + switch (res) { + case 0: + if (selected) { + child = item_data(); + if (!child->sym) + break; + + sym_set_tristate_value(child->sym, yes); + } + return; + case 1: + if (selected) { + child = item_data(); + show_help(child); + active = child->sym; + } else + show_help(menu); + break; + case KEY_ESC: + return; + case -ERRDISPLAYTOOSMALL: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + int res; + const char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = _(inputbox_instructions_int); + break; + case S_HEX: + heading = _(inputbox_instructions_hex); + break; + case S_STRING: + heading = _(inputbox_instructions_string); + break; + default: + heading = _("Internal mconf error!"); + } + dialog_clear(); + res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"), + heading, 10, 75, + sym_get_string_value(menu->sym)); + switch (res) { + case 0: + if (sym_set_string_value(menu->sym, dialog_input_result)) + return; + show_textbox(NULL, _("You have made an invalid entry."), 5, 43); + break; + case 1: + show_help(menu); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_load(void) +{ + + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, load_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) { + set_config_filename(dialog_input_result); + sym_set_change_count(1); + return; + } + show_textbox(NULL, _("File does not exist!"), 5, 38); + break; + case 1: + show_helptext(_("Load Alternate Configuration"), load_config_help); + break; + case KEY_ESC: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + int res; + dialog_clear(); + res = dialog_inputbox(NULL, save_config_text, + 11, 55, filename); + switch(res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_write(dialog_input_result)) { + set_config_filename(dialog_input_result); + return; + } + show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60); + break; + case 1: + show_helptext(_("Save Alternate Configuration"), save_config_help); + break; + case KEY_ESC: + return; + } + } +} + +static int handle_exit(void) +{ + int res; + + save_and_exit = 1; + reset_subtitle(); + dialog_clear(); + if (conf_get_changed()) + res = dialog_yesno(NULL, + _("Do you wish to save your new configuration?\n" + "(Press to continue kernel configuration.)"), + 6, 60); + else + res = -1; + + end_dialog(saved_x, saved_y); + + switch (res) { + case 0: + if (conf_write(filename)) { + fprintf(stderr, _("\n\n" + "Error while writing of the configuration.\n" + "Your configuration changes were NOT saved." + "\n\n")); + return 1; + } + /* fall through */ + case -1: + if (!silent) + printf(_("\n\n" + "*** End of the configuration.\n" + "*** Execute 'make' to start the build or try 'make help'." + "\n\n")); + res = 0; + break; + default: + if (!silent) + fprintf(stderr, _("\n\n" + "Your configuration changes were NOT saved." + "\n\n")); + if (res != KEY_ESC) + res = 0; + } + + return res; +} + +static void sig_handler(int signo) +{ + exit(handle_exit()); +} + +int main(int ac, char **av) +{ + char *mode; + int res; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + signal(SIGINT, sig_handler); + + if (ac > 1 && strcmp(av[1], "-s") == 0) { + silent = 1; + /* Silence conf_read() until the real callback is set up */ + conf_set_message_callback(NULL); + av++; + } + conf_parse(av[1]); + conf_read(NULL); + + mode = getenv("MENUCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + if (init_dialog(NULL)) { + fprintf(stderr, N_("Your display is too small to run Menuconfig!\n")); + fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n")); + return 1; + } + + set_config_filename(conf_get_configname()); + conf_set_message_callback(conf_message_callback); + do { + conf(&rootmenu, NULL); + res = handle_exit(); + } while (res == KEY_ESC); + + return res; +} diff --git a/tools/kconfig/menu.c b/tools/kconfig/menu.c new file mode 100644 index 0000000..e935793 --- /dev/null +++ b/tools/kconfig/menu.c @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include + +#include "lkc.h" + +static const char nohelp_text[] = "There is no help available for this option."; + +struct menu rootmenu; +static struct menu **last_entry_ptr; + +struct file *file_list; +struct file *current_file; + +void menu_warn(struct menu *menu, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +static void prop_warn(struct property *prop, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +void _menu_init(void) +{ + current_entry = current_menu = &rootmenu; + last_entry_ptr = &rootmenu.list; +} + +void menu_add_entry(struct symbol *sym) +{ + struct menu *menu; + + menu = xmalloc(sizeof(*menu)); + memset(menu, 0, sizeof(*menu)); + menu->sym = sym; + menu->parent = current_menu; + menu->file = current_file; + menu->lineno = zconf_lineno(); + + *last_entry_ptr = menu; + last_entry_ptr = &menu->next; + current_entry = menu; + if (sym) + menu_add_symbol(P_SYMBOL, sym, NULL); +} + +void menu_end_entry(void) +{ +} + +struct menu *menu_add_menu(void) +{ + menu_end_entry(); + last_entry_ptr = ¤t_entry->list; + return current_menu = current_entry; +} + +void menu_end_menu(void) +{ + last_entry_ptr = ¤t_menu->next; + current_menu = current_menu->parent; +} + +static struct expr *menu_check_dep(struct expr *e) +{ + if (!e) + return e; + + switch (e->type) { + case E_NOT: + e->left.expr = menu_check_dep(e->left.expr); + break; + case E_OR: + case E_AND: + e->left.expr = menu_check_dep(e->left.expr); + e->right.expr = menu_check_dep(e->right.expr); + break; + case E_SYMBOL: + /* change 'm' into 'm' && MODULES */ + if (e->left.sym == &symbol_mod) + return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); + break; + default: + break; + } + return e; +} + +void menu_add_dep(struct expr *dep) +{ + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +} + +void menu_set_type(int type) +{ + struct symbol *sym = current_entry->sym; + + if (sym->type == type) + return; + if (sym->type == S_UNKNOWN) { + sym->type = type; + return; + } + menu_warn(current_entry, + "ignoring type redefinition of '%s' from '%s' to '%s'", + sym->name ? sym->name : "", + sym_type_name(sym->type), sym_type_name(type)); +} + +static struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +{ + struct property *prop = prop_alloc(type, current_entry->sym); + + prop->menu = current_entry; + prop->expr = expr; + prop->visible.expr = menu_check_dep(dep); + + if (prompt) { + if (isspace(*prompt)) { + prop_warn(prop, "leading whitespace ignored"); + while (isspace(*prompt)) + prompt++; + } + if (current_entry->prompt && current_entry != &rootmenu) + prop_warn(prop, "prompt redefined"); + + /* Apply all upper menus' visibilities to actual prompts. */ + if(type == P_PROMPT) { + struct menu *menu = current_entry; + + while ((menu = menu->parent) != NULL) { + struct expr *dup_expr; + + if (!menu->visibility) + continue; + /* + * Do not add a reference to the + * menu's visibility expression but + * use a copy of it. Otherwise the + * expression reduction functions + * will modify expressions that have + * multiple references which can + * cause unwanted side effects. + */ + dup_expr = expr_copy(menu->visibility); + + prop->visible.expr + = expr_alloc_and(prop->visible.expr, + dup_expr); + } + } + + current_entry->prompt = prop; + } + prop->text = prompt; + + return prop; +} + +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) +{ + return menu_add_prop(type, prompt, NULL, dep); +} + +void menu_add_visibility(struct expr *expr) +{ + current_entry->visibility = expr_alloc_and(current_entry->visibility, + expr); +} + +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) +{ + menu_add_prop(type, NULL, expr, dep); +} + +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) +{ + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); +} + +void menu_add_option(int token, char *arg) +{ + switch (token) { + case T_OPT_MODULES: + if (modules_sym) + zconf_error("symbol '%s' redefines option 'modules'" + " already defined by symbol '%s'", + current_entry->sym->name, + modules_sym->name + ); + modules_sym = current_entry->sym; + break; + case T_OPT_DEFCONFIG_LIST: + if (!sym_defconfig_list) + sym_defconfig_list = current_entry->sym; + else if (sym_defconfig_list != current_entry->sym) + zconf_error("trying to redefine defconfig symbol"); + break; + case T_OPT_ENV: + prop_add_env(arg); + break; + case T_OPT_ALLNOCONFIG_Y: + current_entry->sym->flags |= SYMBOL_ALLNOCONFIG_Y; + break; + } +} + +static int menu_validate_number(struct symbol *sym, struct symbol *sym2) +{ + return sym2->type == S_INT || sym2->type == S_HEX || + (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); +} + +static void sym_check_prop(struct symbol *sym) +{ + struct property *prop; + struct symbol *sym2; + char *use; + + for (prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_DEFAULT: + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && + prop->expr->type != E_SYMBOL) + prop_warn(prop, + "default for config symbol '%s'" + " must be a single symbol", sym->name); + if (prop->expr->type != E_SYMBOL) + break; + sym2 = prop_get_symbol(prop); + if (sym->type == S_HEX || sym->type == S_INT) { + if (!menu_validate_number(sym, sym2)) + prop_warn(prop, + "'%s': number is invalid", + sym->name); + } + break; + case P_SELECT: + case P_IMPLY: + use = prop->type == P_SELECT ? "select" : "imply"; + sym2 = prop_get_symbol(prop); + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) + prop_warn(prop, + "config symbol '%s' uses %s, but is " + "not boolean or tristate", sym->name, use); + else if (sym2->type != S_UNKNOWN && + sym2->type != S_BOOLEAN && + sym2->type != S_TRISTATE) + prop_warn(prop, + "'%s' has wrong type. '%s' only " + "accept arguments of boolean and " + "tristate type", sym2->name, use); + break; + case P_RANGE: + if (sym->type != S_INT && sym->type != S_HEX) + prop_warn(prop, "range is only allowed " + "for int or hex symbols"); + if (!menu_validate_number(sym, prop->expr->left.sym) || + !menu_validate_number(sym, prop->expr->right.sym)) + prop_warn(prop, "range is invalid"); + break; + default: + ; + } + } +} + +void menu_finalize(struct menu *parent) +{ + struct menu *menu, *last_menu; + struct symbol *sym; + struct property *prop; + struct expr *parentdep, *basedep, *dep, *dep2, **ep; + + sym = parent->sym; + if (parent->list) { + if (sym && sym_is_choice(sym)) { + if (sym->type == S_UNKNOWN) { + /* find the first choice value to find out choice type */ + current_entry = parent; + for (menu = parent->list; menu; menu = menu->next) { + if (menu->sym && menu->sym->type != S_UNKNOWN) { + menu_set_type(menu->sym->type); + break; + } + } + } + /* set the type of the remaining choice values */ + for (menu = parent->list; menu; menu = menu->next) { + current_entry = menu; + if (menu->sym && menu->sym->type == S_UNKNOWN) + menu_set_type(sym->type); + } + parentdep = expr_alloc_symbol(sym); + } else if (parent->prompt) + parentdep = parent->prompt->visible.expr; + else + parentdep = parent->dep; + + for (menu = parent->list; menu; menu = menu->next) { + basedep = expr_transform(menu->dep); + basedep = expr_alloc_and(expr_copy(parentdep), basedep); + basedep = expr_eliminate_dups(basedep); + menu->dep = basedep; + if (menu->sym) + prop = menu->sym->prop; + else + prop = menu->prompt; + for (; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + dep = expr_transform(prop->visible.expr); + dep = expr_alloc_and(expr_copy(basedep), dep); + dep = expr_eliminate_dups(dep); + if (menu->sym && menu->sym->type != S_TRISTATE) + dep = expr_trans_bool(dep); + prop->visible.expr = dep; + if (prop->type == P_SELECT) { + struct symbol *es = prop_get_symbol(prop); + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } else if (prop->type == P_IMPLY) { + struct symbol *es = prop_get_symbol(prop); + es->implied.expr = expr_alloc_or(es->implied.expr, + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); + } + } + } + for (menu = parent->list; menu; menu = menu->next) + menu_finalize(menu); + } else if (sym) { + basedep = parent->prompt ? parent->prompt->visible.expr : NULL; + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); + basedep = expr_eliminate_dups(expr_transform(basedep)); + last_menu = NULL; + for (menu = parent->next; menu; menu = menu->next) { + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; + if (!expr_contains_symbol(dep, sym)) + break; + if (expr_depends_symbol(dep, sym)) + goto next; + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); + dep = expr_eliminate_dups(expr_transform(dep)); + dep2 = expr_copy(basedep); + expr_eliminate_eq(&dep, &dep2); + expr_free(dep); + if (!expr_is_yes(dep2)) { + expr_free(dep2); + break; + } + expr_free(dep2); + next: + menu_finalize(menu); + menu->parent = parent; + last_menu = menu; + } + if (last_menu) { + parent->list = parent->next; + parent->next = last_menu->next; + last_menu->next = NULL; + } + + sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); + } + for (menu = parent->list; menu; menu = menu->next) { + if (sym && sym_is_choice(sym) && + menu->sym && !sym_is_choice_value(menu->sym)) { + current_entry = menu; + menu->sym->flags |= SYMBOL_CHOICEVAL; + if (!menu->prompt) + menu_warn(menu, "choice value must have a prompt"); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->type == P_DEFAULT) + prop_warn(prop, "defaults for choice " + "values not supported"); + if (prop->menu == menu) + continue; + if (prop->type == P_PROMPT && + prop->menu->parent->sym != sym) + prop_warn(prop, "choice value used outside its choice group"); + } + /* Non-tristate choice values of tristate choices must + * depend on the choice being set to Y. The choice + * values' dependencies were propagated to their + * properties above, so the change here must be re- + * propagated. + */ + if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { + basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); + menu->dep = expr_alloc_and(basedep, menu->dep); + for (prop = menu->sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + prop->visible.expr = expr_alloc_and(expr_copy(basedep), + prop->visible.expr); + } + } + menu_add_symbol(P_CHOICE, sym, NULL); + prop = sym_get_choice_prop(sym); + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) + ; + *ep = expr_alloc_one(E_LIST, NULL); + (*ep)->right.sym = menu->sym; + } + if (menu->list && (!menu->prompt || !menu->prompt->text)) { + for (last_menu = menu->list; ; last_menu = last_menu->next) { + last_menu->parent = parent; + if (!last_menu->next) + break; + } + last_menu->next = menu->next; + menu->next = menu->list; + menu->list = NULL; + } + } + + if (sym && !(sym->flags & SYMBOL_WARNED)) { + if (sym->type == S_UNKNOWN) + menu_warn(parent, "config symbol defined without type"); + + if (sym_is_choice(sym) && !parent->prompt) + menu_warn(parent, "choice must have a prompt"); + + /* Check properties connected to this symbol */ + sym_check_prop(sym); + sym->flags |= SYMBOL_WARNED; + } + + if (sym && !sym_is_optional(sym) && parent->prompt) { + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, + expr_alloc_and(parent->prompt->visible.expr, + expr_alloc_symbol(&symbol_mod))); + } +} + +bool menu_has_prompt(struct menu *menu) +{ + if (!menu->prompt) + return false; + return true; +} + +/* + * Determine if a menu is empty. + * A menu is considered empty if it contains no or only + * invisible entries. + */ +bool menu_is_empty(struct menu *menu) +{ + struct menu *child; + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child)) + return(false); + } + return(true); +} + +bool menu_is_visible(struct menu *menu) +{ + struct menu *child; + struct symbol *sym; + tristate visible; + + if (!menu->prompt) + return false; + + if (menu->visibility) { + if (expr_calc_value(menu->visibility) == no) + return false; + } + + sym = menu->sym; + if (sym) { + sym_calc_value(sym); + visible = menu->prompt->visible.tri; + } else + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); + + if (visible != no) + return true; + + if (!sym || sym_get_tristate_value(menu->sym) == no) + return false; + + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child)) { + if (sym) + sym->flags |= SYMBOL_DEF_USER; + return true; + } + } + + return false; +} + +const char *menu_get_prompt(struct menu *menu) +{ + if (menu->prompt) + return menu->prompt->text; + else if (menu->sym) + return menu->sym->name; + return NULL; +} + +struct menu *menu_get_root_menu(struct menu *menu) +{ + return &rootmenu; +} + +struct menu *menu_get_parent_menu(struct menu *menu) +{ + enum prop_type type; + + for (; menu != &rootmenu; menu = menu->parent) { + type = menu->prompt ? menu->prompt->type : 0; + if (type == P_MENU) + break; + } + return menu; +} + +bool menu_has_help(struct menu *menu) +{ + return menu->help != NULL; +} + +const char *menu_get_help(struct menu *menu) +{ + if (menu->help) + return menu->help; + else + return ""; +} + +static void get_prompt_str(struct gstr *r, struct property *prop, + struct list_head *head) +{ + int i, j; + struct menu *submenu[8], *menu, *location = NULL; + struct jump_key *jump = NULL; + + str_printf(r, _("Prompt: %s\n"), _(prop->text)); + menu = prop->menu->parent; + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { + bool accessible = menu_is_visible(menu); + + submenu[i++] = menu; + if (location == NULL && accessible) + location = menu; + } + if (head && location) { + jump = xmalloc(sizeof(struct jump_key)); + + if (menu_is_visible(prop->menu)) { + /* + * There is not enough room to put the hint at the + * beginning of the "Prompt" line. Put the hint on the + * last "Location" line even when it would belong on + * the former. + */ + jump->target = prop->menu; + } else + jump->target = location; + + if (list_empty(head)) + jump->index = 0; + else + jump->index = list_entry(head->prev, struct jump_key, + entries)->index + 1; + + list_add_tail(&jump->entries, head); + } + + if (i > 0) { + str_printf(r, _(" Location:\n")); + for (j = 4; --i >= 0; j += 2) { + menu = submenu[i]; + if (jump && menu == location) + jump->offset = strlen(r->s); + str_printf(r, "%*c-> %s", j, ' ', + _(menu_get_prompt(menu))); + if (menu->sym) { + str_printf(r, " (%s [=%s])", menu->sym->name ? + menu->sym->name : _(""), + sym_get_string_value(menu->sym)); + } + str_append(r, "\n"); + } + } +} + +/* + * get property of type P_SYMBOL + */ +static struct property *get_symbol_prop(struct symbol *sym) +{ + struct property *prop = NULL; + + for_all_properties(sym, prop, P_SYMBOL) + break; + return prop; +} + +static void get_symbol_props_str(struct gstr *r, struct symbol *sym, + enum prop_type tok, const char *prefix) +{ + bool hit = false; + struct property *prop; + + for_all_properties(sym, prop, tok) { + if (!hit) { + str_append(r, prefix); + hit = true; + } else + str_printf(r, " && "); + expr_gstr_print(prop->expr, r); + } + if (hit) + str_append(r, "\n"); +} + +/* + * head is optional and may be NULL + */ +static void get_symbol_str(struct gstr *r, struct symbol *sym, + struct list_head *head) +{ + struct property *prop; + + if (sym && sym->name) { + str_printf(r, "Symbol: %s [=%s]\n", sym->name, + sym_get_string_value(sym)); + str_printf(r, "Type : %s\n", sym_type_name(sym->type)); + if (sym->type == S_INT || sym->type == S_HEX) { + prop = sym_get_range_prop(sym); + if (prop) { + str_printf(r, "Range : "); + expr_gstr_print(prop->expr, r); + str_append(r, "\n"); + } + } + } + for_all_prompts(sym, prop) + get_prompt_str(r, prop, head); + + prop = get_symbol_prop(sym); + if (prop) { + str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, + prop->menu->lineno); + if (!expr_is_yes(prop->visible.expr)) { + str_append(r, _(" Depends on: ")); + expr_gstr_print(prop->visible.expr, r); + str_append(r, "\n"); + } + } + + get_symbol_props_str(r, sym, P_SELECT, _(" Selects: ")); + if (sym->rev_dep.expr) { + str_append(r, _(" Selected by: ")); + expr_gstr_print(sym->rev_dep.expr, r); + str_append(r, "\n"); + } + + get_symbol_props_str(r, sym, P_IMPLY, _(" Implies: ")); + if (sym->implied.expr) { + str_append(r, _(" Implied by: ")); + expr_gstr_print(sym->implied.expr, r); + str_append(r, "\n"); + } + + str_append(r, "\n\n"); +} + +struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head) +{ + struct symbol *sym; + struct gstr res = str_new(); + int i; + + for (i = 0; sym_arr && (sym = sym_arr[i]); i++) + get_symbol_str(&res, sym, head); + if (!i) + str_append(&res, _("No matches found.\n")); + return res; +} + + +void menu_get_ext_help(struct menu *menu, struct gstr *help) +{ + struct symbol *sym = menu->sym; + const char *help_text = nohelp_text; + + if (menu_has_help(menu)) { + if (sym->name) + str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); + help_text = menu_get_help(menu); + } + str_printf(help, "%s\n", _(help_text)); + if (sym) + get_symbol_str(help, sym, NULL); +} diff --git a/tools/kconfig/merge_config.sh b/tools/kconfig/merge_config.sh new file mode 100755 index 0000000..67d1314 --- /dev/null +++ b/tools/kconfig/merge_config.sh @@ -0,0 +1,170 @@ +#!/bin/sh +# merge_config.sh - Takes a list of config fragment values, and merges +# them one by one. Provides warnings on overridden values, and specified +# values that did not make it to the resulting .config file (due to missed +# dependencies or config symbol removal). +# +# Portions reused from kconf_check and generate_cfg: +# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/kconf_check +# http://git.yoctoproject.org/cgit/cgit.cgi/yocto-kernel-tools/tree/tools/generate_cfg +# +# Copyright (c) 2009-2010 Wind River Systems, Inc. +# Copyright 2011 Linaro +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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. + +clean_up() { + rm -f $TMP_FILE + exit +} +trap clean_up HUP INT TERM + +usage() { + echo "Usage: $0 [OPTIONS] [CONFIG [...]]" + echo " -h display this help text" + echo " -m only merge the fragments, do not execute the make command" + echo " -n use allnoconfig instead of alldefconfig" + echo " -r list redundant entries when merging fragments" + echo " -O dir to put generated output files. Consider setting \$KCONFIG_CONFIG instead." +} + +RUNMAKE=true +ALLTARGET=alldefconfig +WARNREDUN=false +OUTPUT=. + +while true; do + case $1 in + "-n") + ALLTARGET=allnoconfig + shift + continue + ;; + "-m") + RUNMAKE=false + shift + continue + ;; + "-h") + usage + exit + ;; + "-r") + WARNREDUN=true + shift + continue + ;; + "-O") + if [ -d $2 ];then + OUTPUT=$(echo $2 | sed 's/\/*$//') + else + echo "output directory $2 does not exist" 1>&2 + exit 1 + fi + shift 2 + continue + ;; + *) + break + ;; + esac +done + +if [ "$#" -lt 1 ] ; then + usage + exit +fi + +if [ -z "$KCONFIG_CONFIG" ]; then + if [ "$OUTPUT" != . ]; then + KCONFIG_CONFIG=$(readlink -m -- "$OUTPUT/.config") + else + KCONFIG_CONFIG=.config + fi +fi + +INITFILE=$1 +shift; + +if [ ! -r "$INITFILE" ]; then + echo "The base file '$INITFILE' does not exist. Exit." >&2 + exit 1 +fi + +MERGE_LIST=$* +SED_CONFIG_EXP="s/^\(# \)\{0,1\}\(CONFIG_[a-zA-Z0-9_]*\)[= ].*/\2/p" +TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) + +echo "Using $INITFILE as base" +cat $INITFILE > $TMP_FILE + +# Merge files, printing warnings on overridden values +for MERGE_FILE in $MERGE_LIST ; do + echo "Merging $MERGE_FILE" + if [ ! -r "$MERGE_FILE" ]; then + echo "The merge file '$MERGE_FILE' does not exist. Exit." >&2 + exit 1 + fi + CFG_LIST=$(sed -n "$SED_CONFIG_EXP" $MERGE_FILE) + + for CFG in $CFG_LIST ; do + grep -q -w $CFG $TMP_FILE || continue + PREV_VAL=$(grep -w $CFG $TMP_FILE) + NEW_VAL=$(grep -w $CFG $MERGE_FILE) + if [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then + echo Value of $CFG is redefined by fragment $MERGE_FILE: + echo Previous value: $PREV_VAL + echo New value: $NEW_VAL + echo + elif [ "$WARNREDUN" = "true" ]; then + echo Value of $CFG is redundant by fragment $MERGE_FILE: + fi + sed -i "/$CFG[ =]/d" $TMP_FILE + done + cat $MERGE_FILE >> $TMP_FILE +done + +if [ "$RUNMAKE" = "false" ]; then + cp -T -- "$TMP_FILE" "$KCONFIG_CONFIG" + echo "#" + echo "# merged configuration written to $KCONFIG_CONFIG (needs make)" + echo "#" + clean_up + exit +fi + +# If we have an output dir, setup the O= argument, otherwise leave +# it blank, since O=. will create an unnecessary ./source softlink +OUTPUT_ARG="" +if [ "$OUTPUT" != "." ] ; then + OUTPUT_ARG="O=$OUTPUT" +fi + + +# Use the merged file as the starting point for: +# alldefconfig: Fills in any missing symbols with Kconfig default +# allnoconfig: Fills in any missing symbols with # CONFIG_* is not set +make KCONFIG_ALLCONFIG=$TMP_FILE $OUTPUT_ARG $ALLTARGET + + +# Check all specified config values took (might have missed-dependency issues) +for CFG in $(sed -n "$SED_CONFIG_EXP" $TMP_FILE); do + + REQUESTED_VAL=$(grep -w -e "$CFG" $TMP_FILE) + ACTUAL_VAL=$(grep -w -e "$CFG" "$KCONFIG_CONFIG") + if [ "x$REQUESTED_VAL" != "x$ACTUAL_VAL" ] ; then + echo "Value requested for $CFG not in final .config" + echo "Requested value: $REQUESTED_VAL" + echo "Actual value: $ACTUAL_VAL" + echo "" + fi +done + +clean_up diff --git a/tools/kconfig/nconf.c b/tools/kconfig/nconf.c new file mode 100644 index 0000000..0031147 --- /dev/null +++ b/tools/kconfig/nconf.c @@ -0,0 +1,1563 @@ +/* + * Copyright (C) 2008 Nir Tzachar +#include + +#include "lkc.h" +#include "nconf.h" +#include + +static const char nconf_global_help[] = N_( +"Help windows\n" +"------------\n" +"o Global help: Unless in a data entry window, pressing will give \n" +" you the global help window, which you are just reading.\n" +"\n" +"o A short version of the global help is available by pressing .\n" +"\n" +"o Local help: To get help related to the current menu entry, use any\n" +" of , or if in a data entry window then press .\n" +"\n" +"\n" +"Menu entries\n" +"------------\n" +"This interface lets you select features and parameters for the kernel\n" +"build. Kernel features can either be built-in, modularized, or removed.\n" +"Parameters must be entered as text or decimal or hexadecimal numbers.\n" +"\n" +"Menu entries beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized, are selected by another feature\n" +" - - are selected by another feature\n" +" XXX cannot be selected. Symbol Info tells you why.\n" +"*, M or whitespace inside braces means to build in, build as a module\n" +"or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the movement keys\n" +"listed below and press to build it in, to make it a module or\n" +" to remove it. You may press the key to cycle through the\n" +"available options.\n" +"\n" +"A trailing \"--->\" designates a submenu, a trailing \"----\" an\n" +"empty submenu.\n" +"\n" +"Menu navigation keys\n" +"----------------------------------------------------------------------\n" +"Linewise up \n" +"Linewise down \n" +"Pagewise up \n" +"Pagewise down \n" +"First entry \n" +"Last entry \n" +"Enter a submenu \n" +"Go back to parent menu \n" +"Close a help window \n" +"Close entry window, apply \n" +"Close entry window, forget \n" +"Start incremental, case-insensitive search for STRING in menu entries,\n" +" no regex support, STRING is displayed in upper left corner\n" +" STRING\n" +" Remove last character \n" +" Jump to next hit \n" +" Jump to previous hit \n" +"Exit menu search mode \n" +"Search for configuration variables with or without leading CONFIG_\n" +" RegExpr\n" +"Verbose search help \n" +"----------------------------------------------------------------------\n" +"\n" +"Unless in a data entry window, key <1> may be used instead of ,\n" +"<2> instead of , etc.\n" +"\n" +"\n" +"Radiolist (Choice list)\n" +"-----------------------\n" +"Use the movement keys listed above to select the option you wish to set\n" +"and press .\n" +"\n" +"\n" +"Data entry\n" +"----------\n" +"Enter the requested information and press . Hexadecimal values\n" +"may be entered without the \"0x\" prefix.\n" +"\n" +"\n" +"Text Box (Help Window)\n" +"----------------------\n" +"Use movement keys as listed in table above.\n" +"\n" +"Press any of to exit.\n" +"\n" +"\n" +"Alternate configuration files\n" +"-----------------------------\n" +"nconfig supports switching between different configurations.\n" +"Press to save your current configuration. Press and enter\n" +"a file name to load a previously saved configuration.\n" +"\n" +"\n" +"Terminal configuration\n" +"----------------------\n" +"If you use nconfig in a xterm window, make sure your TERM environment\n" +"variable specifies a terminal configuration which supports at least\n" +"16 colors. Otherwise nconfig will look rather bad.\n" +"\n" +"If the \"stty size\" command reports the current terminalsize correctly,\n" +"nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n" +"and display longer menus properly.\n" +"\n" +"\n" +"Single menu mode\n" +"----------------\n" +"If you prefer to have all of the menu entries listed in a single menu,\n" +"rather than the default multimenu hierarchy, run nconfig with\n" +"NCONFIG_MODE environment variable set to single_menu. Example:\n" +"\n" +"make NCONFIG_MODE=single_menu nconfig\n" +"\n" +" will then unfold the appropriate category, or fold it if it\n" +"is already unfolded. Folded menu entries will be designated by a\n" +"leading \"++>\" and unfolded entries by a leading \"-->\".\n" +"\n" +"Note that this mode can eventually be a little more CPU expensive than\n" +"the default mode, especially with a larger number of unfolded submenus.\n" +"\n"), +menu_no_f_instructions[] = N_( +"Legend: [*] built-in [ ] excluded module < > module capable.\n" +"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with and .\n" +"Enter a submenu with or .\n" +"Exit a submenu to its parent menu with or .\n" +"Pressing includes, excludes, modularizes features.\n" +"Pressing cycles through the available options.\n" +"To search for menu entries press .\n" +" always leaves the current window.\n" +"\n" +"You do not have function keys support.\n" +"Press <1> instead of , <2> instead of , etc.\n" +"For verbose global help use key <1>.\n" +"For help related to the current menu entry press or .\n"), +menu_instructions[] = N_( +"Legend: [*] built-in [ ] excluded module < > module capable.\n" +"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with or .\n" +"Enter a submenu with or .\n" +"Exit a submenu to its parent menu with or .\n" +"Pressing includes, excludes, modularizes features.\n" +"Pressing cycles through the available options.\n" +"To search for menu entries press .\n" +" always leaves the current window.\n" +"\n" +"Pressing <1> may be used instead of , <2> instead of , etc.\n" +"For verbose global help press .\n" +"For help related to the current menu entry press or .\n"), +radiolist_instructions[] = N_( +"Press , , or to navigate a radiolist, select\n" +"with .\n" +"For help related to the current entry press or .\n" +"For global help press .\n"), +inputbox_instructions_int[] = N_( +"Please enter a decimal value.\n" +"Fractions will not be accepted.\n" +"Press to apply, to cancel."), +inputbox_instructions_hex[] = N_( +"Please enter a hexadecimal value.\n" +"Press to apply, to cancel."), +inputbox_instructions_string[] = N_( +"Please enter a string value.\n" +"Press to apply, to cancel."), +setmod_text[] = N_( +"This feature depends on another feature which has been configured as a\n" +"module. As a result, the current feature will be built as a module too."), +load_config_text[] = N_( +"Enter the name of the configuration file you wish to load.\n" +"Accept the name shown to restore the configuration you last\n" +"retrieved. Leave empty to abort."), +load_config_help[] = N_( +"For various reasons, one may wish to keep several different\n" +"configurations available on a single machine.\n" +"\n" +"If you have saved a previous configuration in a file other than the\n" +"default one, entering its name here will allow you to load and modify\n" +"that configuration.\n" +"\n" +"Leave empty to abort.\n"), +save_config_text[] = N_( +"Enter a filename to which this configuration should be saved\n" +"as an alternate. Leave empty to abort."), +save_config_help[] = N_( +"For various reasons, one may wish to keep several different\n" +"configurations available on a single machine.\n" +"\n" +"Entering a file name here will allow you to later retrieve, modify\n" +"and use the current configuration as an alternate to whatever\n" +"configuration options you have selected at that time.\n" +"\n" +"Leave empty to abort.\n"), +search_help[] = N_( +"Search for symbols (configuration variable names CONFIG_*) and display\n" +"their relations. Regular expressions are supported.\n" +"Example: Search for \"^FOO\".\n" +"Result:\n" +"-----------------------------------------------------------------\n" +"Symbol: FOO [ = m]\n" +"Prompt: Foo bus is used to drive the bar HW\n" +"Defined at drivers/pci/Kconfig:47\n" +"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" +"Location:\n" +" -> Bus options (PCI, PCMCIA, EISA, ISA)\n" +" -> PCI support (PCI [ = y])\n" +" -> PCI access mode ( [ = y])\n" +"Selects: LIBCRC32\n" +"Selected by: BAR\n" +"-----------------------------------------------------------------\n" +"o The line 'Prompt:' shows the text displayed for this symbol in\n" +" the menu hierarchy.\n" +"o The 'Defined at' line tells at what file / line number the symbol is\n" +" defined.\n" +"o The 'Depends on:' line lists symbols that need to be defined for\n" +" this symbol to be visible and selectable in the menu.\n" +"o The 'Location:' lines tell, where in the menu structure this symbol\n" +" is located. A location followed by a [ = y] indicates that this is\n" +" a selectable menu item, and the current value is displayed inside\n" +" brackets.\n" +"o The 'Selects:' line tells, what symbol will be automatically selected\n" +" if this symbol is selected (y or m).\n" +"o The 'Selected by' line tells what symbol has selected this symbol.\n" +"\n" +"Only relevant lines are shown.\n" +"\n\n" +"Search examples:\n" +"USB => find all symbols containing USB\n" +"^USB => find all symbols starting with USB\n" +"USB$ => find all symbols ending with USB\n" +"\n"); + +struct mitem { + char str[256]; + char tag; + void *usrptr; + int is_visible; +}; + +#define MAX_MENU_ITEMS 4096 +static int show_all_items; +static int indent; +static struct menu *current_menu; +static int child_count; +static int single_menu_mode; +/* the window in which all information appears */ +static WINDOW *main_window; +/* the largest size of the menu window */ +static int mwin_max_lines; +static int mwin_max_cols; +/* the window in which we show option buttons */ +static MENU *curses_menu; +static ITEM *curses_menu_items[MAX_MENU_ITEMS]; +static struct mitem k_menu_items[MAX_MENU_ITEMS]; +static int items_num; +static int global_exit; +/* the currently selected button */ +static const char *current_instructions = menu_instructions; + +static char *dialog_input_result; +static int dialog_input_result_len; + +static void conf(struct menu *menu); +static void conf_choice(struct menu *menu); +static void conf_string(struct menu *menu); +static void conf_load(void); +static void conf_save(void); +static void show_help(struct menu *menu); +static int do_exit(void); +static void setup_windows(void); +static void search_conf(void); + +typedef void (*function_key_handler_t)(int *key, struct menu *menu); +static void handle_f1(int *key, struct menu *current_item); +static void handle_f2(int *key, struct menu *current_item); +static void handle_f3(int *key, struct menu *current_item); +static void handle_f4(int *key, struct menu *current_item); +static void handle_f5(int *key, struct menu *current_item); +static void handle_f6(int *key, struct menu *current_item); +static void handle_f7(int *key, struct menu *current_item); +static void handle_f8(int *key, struct menu *current_item); +static void handle_f9(int *key, struct menu *current_item); + +struct function_keys { + const char *key_str; + const char *func; + function_key key; + function_key_handler_t handler; +}; + +static const int function_keys_num = 9; +static struct function_keys function_keys[] = { + { + .key_str = "F1", + .func = "Help", + .key = F_HELP, + .handler = handle_f1, + }, + { + .key_str = "F2", + .func = "SymInfo", + .key = F_SYMBOL, + .handler = handle_f2, + }, + { + .key_str = "F3", + .func = "Help 2", + .key = F_INSTS, + .handler = handle_f3, + }, + { + .key_str = "F4", + .func = "ShowAll", + .key = F_CONF, + .handler = handle_f4, + }, + { + .key_str = "F5", + .func = "Back", + .key = F_BACK, + .handler = handle_f5, + }, + { + .key_str = "F6", + .func = "Save", + .key = F_SAVE, + .handler = handle_f6, + }, + { + .key_str = "F7", + .func = "Load", + .key = F_LOAD, + .handler = handle_f7, + }, + { + .key_str = "F8", + .func = "SymSearch", + .key = F_SEARCH, + .handler = handle_f8, + }, + { + .key_str = "F9", + .func = "Exit", + .key = F_EXIT, + .handler = handle_f9, + }, +}; + +static void print_function_line(void) +{ + int i; + int offset = 1; + const int skip = 1; + int lines = getmaxy(stdscr); + + for (i = 0; i < function_keys_num; i++) { + (void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]); + mvwprintw(main_window, lines-3, offset, + "%s", + function_keys[i].key_str); + (void) wattrset(main_window, attributes[FUNCTION_TEXT]); + offset += strlen(function_keys[i].key_str); + mvwprintw(main_window, lines-3, + offset, "%s", + function_keys[i].func); + offset += strlen(function_keys[i].func) + skip; + } + (void) wattrset(main_window, attributes[NORMAL]); +} + +/* help */ +static void handle_f1(int *key, struct menu *current_item) +{ + show_scroll_win(main_window, + _("Global help"), _(nconf_global_help)); + return; +} + +/* symbole help */ +static void handle_f2(int *key, struct menu *current_item) +{ + show_help(current_item); + return; +} + +/* instructions */ +static void handle_f3(int *key, struct menu *current_item) +{ + show_scroll_win(main_window, + _("Short help"), + _(current_instructions)); + return; +} + +/* config */ +static void handle_f4(int *key, struct menu *current_item) +{ + int res = btn_dialog(main_window, + _("Show all symbols?"), + 2, + " ", + ""); + if (res == 0) + show_all_items = 1; + else if (res == 1) + show_all_items = 0; + + return; +} + +/* back */ +static void handle_f5(int *key, struct menu *current_item) +{ + *key = KEY_LEFT; + return; +} + +/* save */ +static void handle_f6(int *key, struct menu *current_item) +{ + conf_save(); + return; +} + +/* load */ +static void handle_f7(int *key, struct menu *current_item) +{ + conf_load(); + return; +} + +/* search */ +static void handle_f8(int *key, struct menu *current_item) +{ + search_conf(); + return; +} + +/* exit */ +static void handle_f9(int *key, struct menu *current_item) +{ + do_exit(); + return; +} + +/* return != 0 to indicate the key was handles */ +static int process_special_keys(int *key, struct menu *menu) +{ + int i; + + if (*key == KEY_RESIZE) { + setup_windows(); + return 1; + } + + for (i = 0; i < function_keys_num; i++) { + if (*key == KEY_F(function_keys[i].key) || + *key == '0' + function_keys[i].key){ + function_keys[i].handler(key, menu); + return 1; + } + } + + return 0; +} + +static void clean_items(void) +{ + int i; + for (i = 0; curses_menu_items[i]; i++) + free_item(curses_menu_items[i]); + bzero(curses_menu_items, sizeof(curses_menu_items)); + bzero(k_menu_items, sizeof(k_menu_items)); + items_num = 0; +} + +typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN, + FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f; + +/* return the index of the matched item, or -1 if no such item exists */ +static int get_mext_match(const char *match_str, match_f flag) +{ + int match_start = item_index(current_item(curses_menu)); + int index; + + if (flag == FIND_NEXT_MATCH_DOWN) + ++match_start; + else if (flag == FIND_NEXT_MATCH_UP) + --match_start; + + index = match_start; + index = (index + items_num) % items_num; + while (true) { + char *str = k_menu_items[index].str; + if (strcasestr(str, match_str) != NULL) + return index; + if (flag == FIND_NEXT_MATCH_UP || + flag == MATCH_TINKER_PATTERN_UP) + --index; + else + ++index; + index = (index + items_num) % items_num; + if (index == match_start) + return -1; + } +} + +/* Make a new item. */ +static void item_make(struct menu *menu, char tag, const char *fmt, ...) +{ + va_list ap; + + if (items_num > MAX_MENU_ITEMS-1) + return; + + bzero(&k_menu_items[items_num], sizeof(k_menu_items[0])); + k_menu_items[items_num].tag = tag; + k_menu_items[items_num].usrptr = menu; + if (menu != NULL) + k_menu_items[items_num].is_visible = + menu_is_visible(menu); + else + k_menu_items[items_num].is_visible = 1; + + va_start(ap, fmt); + vsnprintf(k_menu_items[items_num].str, + sizeof(k_menu_items[items_num].str), + fmt, ap); + va_end(ap); + + if (!k_menu_items[items_num].is_visible) + memcpy(k_menu_items[items_num].str, "XXX", 3); + + curses_menu_items[items_num] = new_item( + k_menu_items[items_num].str, + k_menu_items[items_num].str); + set_item_userptr(curses_menu_items[items_num], + &k_menu_items[items_num]); + /* + if (!k_menu_items[items_num].is_visible) + item_opts_off(curses_menu_items[items_num], O_SELECTABLE); + */ + + items_num++; + curses_menu_items[items_num] = NULL; +} + +/* very hackish. adds a string to the last item added */ +static void item_add_str(const char *fmt, ...) +{ + va_list ap; + int index = items_num-1; + char new_str[256]; + char tmp_str[256]; + + if (index < 0) + return; + + va_start(ap, fmt); + vsnprintf(new_str, sizeof(new_str), fmt, ap); + va_end(ap); + snprintf(tmp_str, sizeof(tmp_str), "%s%s", + k_menu_items[index].str, new_str); + strncpy(k_menu_items[index].str, + tmp_str, + sizeof(k_menu_items[index].str)); + + free_item(curses_menu_items[index]); + curses_menu_items[index] = new_item( + k_menu_items[index].str, + k_menu_items[index].str); + set_item_userptr(curses_menu_items[index], + &k_menu_items[index]); +} + +/* get the tag of the currently selected item */ +static char item_tag(void) +{ + ITEM *cur; + struct mitem *mcur; + + cur = current_item(curses_menu); + if (cur == NULL) + return 0; + mcur = (struct mitem *) item_userptr(cur); + return mcur->tag; +} + +static int curses_item_index(void) +{ + return item_index(current_item(curses_menu)); +} + +static void *item_data(void) +{ + ITEM *cur; + struct mitem *mcur; + + cur = current_item(curses_menu); + if (!cur) + return NULL; + mcur = (struct mitem *) item_userptr(cur); + return mcur->usrptr; + +} + +static int item_is_tag(char tag) +{ + return item_tag() == tag; +} + +static char filename[PATH_MAX+1]; +static char menu_backtitle[PATH_MAX+128]; +static const char *set_config_filename(const char *config_filename) +{ + int size; + + size = snprintf(menu_backtitle, sizeof(menu_backtitle), + "%s - %s", config_filename, rootmenu.prompt->text); + if (size >= sizeof(menu_backtitle)) + menu_backtitle[sizeof(menu_backtitle)-1] = '\0'; + + size = snprintf(filename, sizeof(filename), "%s", config_filename); + if (size >= sizeof(filename)) + filename[sizeof(filename)-1] = '\0'; + return menu_backtitle; +} + +/* return = 0 means we are successful. + * -1 means go on doing what you were doing + */ +static int do_exit(void) +{ + int res; + if (!conf_get_changed()) { + global_exit = 1; + return 0; + } + res = btn_dialog(main_window, + _("Do you wish to save your new configuration?\n" + " to cancel and resume nconfig."), + 2, + " ", + ""); + if (res == KEY_EXIT) { + global_exit = 0; + return -1; + } + + /* if we got here, the user really wants to exit */ + switch (res) { + case 0: + res = conf_write(filename); + if (res) + btn_dialog( + main_window, + _("Error during writing of configuration.\n" + "Your configuration changes were NOT saved."), + 1, + ""); + break; + default: + btn_dialog( + main_window, + _("Your configuration changes were NOT saved."), + 1, + ""); + break; + } + global_exit = 1; + return 0; +} + + +static void search_conf(void) +{ + struct symbol **sym_arr; + struct gstr res; + struct gstr title; + char *dialog_input; + int dres; + + title = str_new(); + str_printf( &title, _("Enter (sub)string or regexp to search for " + "(with or without \"%s\")"), CONFIG_); + +again: + dres = dialog_inputbox(main_window, + _("Search Configuration Parameter"), + str_get(&title), + "", &dialog_input_result, &dialog_input_result_len); + switch (dres) { + case 0: + break; + case 1: + show_scroll_win(main_window, + _("Search Configuration"), search_help); + goto again; + default: + str_free(&title); + return; + } + + /* strip the prefix if necessary */ + dialog_input = dialog_input_result; + if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0) + dialog_input += strlen(CONFIG_); + + sym_arr = sym_re_search(dialog_input); + res = get_relations_str(sym_arr, NULL); + free(sym_arr); + show_scroll_win(main_window, + _("Search Results"), str_get(&res)); + str_free(&res); + str_free(&title); +} + + +static void build_conf(struct menu *menu) +{ + struct symbol *sym; + struct property *prop; + struct menu *child; + int type, tmp, doint = 2; + tristate val; + char ch; + + if (!menu || (!show_all_items && !menu_is_visible(menu))) + return; + + sym = menu->sym; + prop = menu->prompt; + if (!sym) { + if (prop && menu != current_menu) { + const char *prompt = menu_get_prompt(menu); + enum prop_type ptype; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + switch (ptype) { + case P_MENU: + child_count++; + prompt = _(prompt); + if (single_menu_mode) { + item_make(menu, 'm', + "%s%*c%s", + menu->data ? "-->" : "++>", + indent + 1, ' ', prompt); + } else + item_make(menu, 'm', + " %*c%s %s", + indent + 1, ' ', prompt, + menu_is_empty(menu) ? "----" : "--->"); + + if (single_menu_mode && menu->data) + goto conf_childs; + return; + case P_COMMENT: + if (prompt) { + child_count++; + item_make(menu, ':', + " %*c*** %s ***", + indent + 1, ' ', + _(prompt)); + } + break; + default: + if (prompt) { + child_count++; + item_make(menu, ':', "---%*c%s", + indent + 1, ' ', + _(prompt)); + } + } + } else + doint = 0; + goto conf_childs; + } + + type = sym_get_type(sym); + if (sym_is_choice(sym)) { + struct symbol *def_sym = sym_get_choice_value(sym); + struct menu *def_menu = NULL; + + child_count++; + for (child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + val = sym_get_tristate_value(sym); + if (sym_is_changable(sym)) { + switch (type) { + case S_BOOLEAN: + item_make(menu, 't', "[%c]", + val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: + ch = '*'; + break; + case mod: + ch = 'M'; + break; + default: + ch = ' '; + break; + } + item_make(menu, 't', "<%c>", ch); + break; + } + } else { + item_make(menu, def_menu ? 't' : ':', " "); + } + + item_add_str("%*c%s", indent + 1, + ' ', _(menu_get_prompt(menu))); + if (val == yes) { + if (def_menu) { + item_add_str(" (%s)", + _(menu_get_prompt(def_menu))); + item_add_str(" --->"); + if (def_menu->list) { + indent += 2; + build_conf(def_menu); + indent -= 2; + } + } + return; + } + } else { + if (menu == current_menu) { + item_make(menu, ':', + "---%*c%s", indent + 1, + ' ', _(menu_get_prompt(menu))); + goto conf_childs; + } + child_count++; + val = sym_get_tristate_value(sym); + if (sym_is_choice_value(sym) && val == yes) { + item_make(menu, ':', " "); + } else { + switch (type) { + case S_BOOLEAN: + if (sym_is_changable(sym)) + item_make(menu, 't', "[%c]", + val == no ? ' ' : '*'); + else + item_make(menu, 't', "-%c-", + val == no ? ' ' : '*'); + break; + case S_TRISTATE: + switch (val) { + case yes: + ch = '*'; + break; + case mod: + ch = 'M'; + break; + default: + ch = ' '; + break; + } + if (sym_is_changable(sym)) { + if (sym->rev_dep.tri == mod) + item_make(menu, + 't', "{%c}", ch); + else + item_make(menu, + 't', "<%c>", ch); + } else + item_make(menu, 't', "-%c-", ch); + break; + default: + tmp = 2 + strlen(sym_get_string_value(sym)); + item_make(menu, 's', " (%s)", + sym_get_string_value(sym)); + tmp = indent - tmp + 4; + if (tmp < 0) + tmp = 0; + item_add_str("%*c%s%s", tmp, ' ', + _(menu_get_prompt(menu)), + (sym_has_value(sym) || + !sym_is_changable(sym)) ? "" : + _(" (NEW)")); + goto conf_childs; + } + } + item_add_str("%*c%s%s", indent + 1, ' ', + _(menu_get_prompt(menu)), + (sym_has_value(sym) || !sym_is_changable(sym)) ? + "" : _(" (NEW)")); + if (menu->prompt && menu->prompt->type == P_MENU) { + item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->"); + return; + } + } + +conf_childs: + indent += doint; + for (child = menu->list; child; child = child->next) + build_conf(child); + indent -= doint; +} + +static void reset_menu(void) +{ + unpost_menu(curses_menu); + clean_items(); +} + +/* adjust the menu to show this item. + * prefer not to scroll the menu if possible*/ +static void center_item(int selected_index, int *last_top_row) +{ + int toprow; + + set_top_row(curses_menu, *last_top_row); + toprow = top_row(curses_menu); + if (selected_index < toprow || + selected_index >= toprow+mwin_max_lines) { + toprow = max(selected_index-mwin_max_lines/2, 0); + if (toprow >= item_count(curses_menu)-mwin_max_lines) + toprow = item_count(curses_menu)-mwin_max_lines; + set_top_row(curses_menu, toprow); + } + set_current_item(curses_menu, + curses_menu_items[selected_index]); + *last_top_row = toprow; + post_menu(curses_menu); + refresh_all_windows(main_window); +} + +/* this function assumes reset_menu has been called before */ +static void show_menu(const char *prompt, const char *instructions, + int selected_index, int *last_top_row) +{ + int maxx, maxy; + WINDOW *menu_window; + + current_instructions = instructions; + + clear(); + (void) wattrset(main_window, attributes[NORMAL]); + print_in_middle(stdscr, 1, 0, getmaxx(stdscr), + menu_backtitle, + attributes[MAIN_HEADING]); + + (void) wattrset(main_window, attributes[MAIN_MENU_BOX]); + box(main_window, 0, 0); + (void) wattrset(main_window, attributes[MAIN_MENU_HEADING]); + mvwprintw(main_window, 0, 3, " %s ", prompt); + (void) wattrset(main_window, attributes[NORMAL]); + + set_menu_items(curses_menu, curses_menu_items); + + /* position the menu at the middle of the screen */ + scale_menu(curses_menu, &maxy, &maxx); + maxx = min(maxx, mwin_max_cols-2); + maxy = mwin_max_lines; + menu_window = derwin(main_window, + maxy, + maxx, + 2, + (mwin_max_cols-maxx)/2); + keypad(menu_window, TRUE); + set_menu_win(curses_menu, menu_window); + set_menu_sub(curses_menu, menu_window); + + /* must reassert this after changing items, otherwise returns to a + * default of 16 + */ + set_menu_format(curses_menu, maxy, 1); + center_item(selected_index, last_top_row); + set_menu_format(curses_menu, maxy, 1); + + print_function_line(); + + /* Post the menu */ + post_menu(curses_menu); + refresh_all_windows(main_window); +} + +static void adj_match_dir(match_f *match_direction) +{ + if (*match_direction == FIND_NEXT_MATCH_DOWN) + *match_direction = + MATCH_TINKER_PATTERN_DOWN; + else if (*match_direction == FIND_NEXT_MATCH_UP) + *match_direction = + MATCH_TINKER_PATTERN_UP; + /* else, do no change.. */ +} + +struct match_state +{ + int in_search; + match_f match_direction; + char pattern[256]; +}; + +/* Return 0 means I have handled the key. In such a case, ans should hold the + * item to center, or -1 otherwise. + * Else return -1 . + */ +static int do_match(int key, struct match_state *state, int *ans) +{ + char c = (char) key; + int terminate_search = 0; + *ans = -1; + if (key == '/' || (state->in_search && key == 27)) { + move(0, 0); + refresh(); + clrtoeol(); + state->in_search = 1-state->in_search; + bzero(state->pattern, sizeof(state->pattern)); + state->match_direction = MATCH_TINKER_PATTERN_DOWN; + return 0; + } else if (!state->in_search) + return 1; + + if (isalnum(c) || isgraph(c) || c == ' ') { + state->pattern[strlen(state->pattern)] = c; + state->pattern[strlen(state->pattern)] = '\0'; + adj_match_dir(&state->match_direction); + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_DOWN) { + state->match_direction = FIND_NEXT_MATCH_DOWN; + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_UP) { + state->match_direction = FIND_NEXT_MATCH_UP; + *ans = get_mext_match(state->pattern, + state->match_direction); + } else if (key == KEY_BACKSPACE || key == 127) { + state->pattern[strlen(state->pattern)-1] = '\0'; + adj_match_dir(&state->match_direction); + } else + terminate_search = 1; + + if (terminate_search) { + state->in_search = 0; + bzero(state->pattern, sizeof(state->pattern)); + move(0, 0); + refresh(); + clrtoeol(); + return -1; + } + return 0; +} + +static void conf(struct menu *menu) +{ + struct menu *submenu = NULL; + const char *prompt = menu_get_prompt(menu); + struct symbol *sym; + int res; + int current_index = 0; + int last_top_row = 0; + struct match_state match_state = { + .in_search = 0, + .match_direction = MATCH_TINKER_PATTERN_DOWN, + .pattern = "", + }; + + while (!global_exit) { + reset_menu(); + current_menu = menu; + build_conf(menu); + if (!child_count) + break; + + show_menu(prompt ? _(prompt) : _("Main Menu"), + _(menu_instructions), + current_index, &last_top_row); + keypad((menu_win(curses_menu)), TRUE); + while (!global_exit) { + if (match_state.in_search) { + mvprintw(0, 0, + "searching: %s", match_state.pattern); + clrtoeol(); + } + refresh_all_windows(main_window); + res = wgetch(menu_win(curses_menu)); + if (!res) + break; + if (do_match(res, &match_state, ¤t_index) == 0) { + if (current_index != -1) + center_item(current_index, + &last_top_row); + continue; + } + if (process_special_keys(&res, + (struct menu *) item_data())) + break; + switch (res) { + case KEY_DOWN: + menu_driver(curses_menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(curses_menu, REQ_UP_ITEM); + break; + case KEY_NPAGE: + menu_driver(curses_menu, REQ_SCR_DPAGE); + break; + case KEY_PPAGE: + menu_driver(curses_menu, REQ_SCR_UPAGE); + break; + case KEY_HOME: + menu_driver(curses_menu, REQ_FIRST_ITEM); + break; + case KEY_END: + menu_driver(curses_menu, REQ_LAST_ITEM); + break; + case 'h': + case '?': + show_help((struct menu *) item_data()); + break; + } + if (res == 10 || res == 27 || + res == 32 || res == 'n' || res == 'y' || + res == KEY_LEFT || res == KEY_RIGHT || + res == 'm') + break; + refresh_all_windows(main_window); + } + + refresh_all_windows(main_window); + /* if ESC or left*/ + if (res == 27 || (menu != &rootmenu && res == KEY_LEFT)) + break; + + /* remember location in the menu */ + last_top_row = top_row(curses_menu); + current_index = curses_item_index(); + + if (!item_tag()) + continue; + + submenu = (struct menu *) item_data(); + if (!submenu || !menu_is_visible(submenu)) + continue; + sym = submenu->sym; + + switch (res) { + case ' ': + if (item_is_tag('t')) + sym_toggle_tristate_value(sym); + else if (item_is_tag('m')) + conf(submenu); + break; + case KEY_RIGHT: + case 10: /* ENTER WAS PRESSED */ + switch (item_tag()) { + case 'm': + if (single_menu_mode) + submenu->data = + (void *) (long) !submenu->data; + else + conf(submenu); + break; + case 't': + if (sym_is_choice(sym) && + sym_get_tristate_value(sym) == yes) + conf_choice(submenu); + else if (submenu->prompt && + submenu->prompt->type == P_MENU) + conf(submenu); + else if (res == 10) + sym_toggle_tristate_value(sym); + break; + case 's': + conf_string(submenu); + break; + } + break; + case 'y': + if (item_is_tag('t')) { + if (sym_set_tristate_value(sym, yes)) + break; + if (sym_set_tristate_value(sym, mod)) + btn_dialog(main_window, setmod_text, 0); + } + break; + case 'n': + if (item_is_tag('t')) + sym_set_tristate_value(sym, no); + break; + case 'm': + if (item_is_tag('t')) + sym_set_tristate_value(sym, mod); + break; + } + } +} + +static void conf_message_callback(const char *fmt, va_list ap) +{ + char buf[1024]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + btn_dialog(main_window, buf, 1, ""); +} + +static void show_help(struct menu *menu) +{ + struct gstr help; + + if (!menu) + return; + + help = str_new(); + menu_get_ext_help(menu, &help); + show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help)); + str_free(&help); +} + +static void conf_choice(struct menu *menu) +{ + const char *prompt = _(menu_get_prompt(menu)); + struct menu *child = NULL; + struct symbol *active; + int selected_index = 0; + int last_top_row = 0; + int res, i = 0; + struct match_state match_state = { + .in_search = 0, + .match_direction = MATCH_TINKER_PATTERN_DOWN, + .pattern = "", + }; + + active = sym_get_choice_value(menu->sym); + /* this is mostly duplicated from the conf() function. */ + while (!global_exit) { + reset_menu(); + + for (i = 0, child = menu->list; child; child = child->next) { + if (!show_all_items && !menu_is_visible(child)) + continue; + + if (child->sym == sym_get_choice_value(menu->sym)) + item_make(child, ':', " %s", + _(menu_get_prompt(child))); + else if (child->sym) + item_make(child, ':', " %s", + _(menu_get_prompt(child))); + else + item_make(child, ':', "*** %s ***", + _(menu_get_prompt(child))); + + if (child->sym == active){ + last_top_row = top_row(curses_menu); + selected_index = i; + } + i++; + } + show_menu(prompt ? _(prompt) : _("Choice Menu"), + _(radiolist_instructions), + selected_index, + &last_top_row); + while (!global_exit) { + if (match_state.in_search) { + mvprintw(0, 0, "searching: %s", + match_state.pattern); + clrtoeol(); + } + refresh_all_windows(main_window); + res = wgetch(menu_win(curses_menu)); + if (!res) + break; + if (do_match(res, &match_state, &selected_index) == 0) { + if (selected_index != -1) + center_item(selected_index, + &last_top_row); + continue; + } + if (process_special_keys( + &res, + (struct menu *) item_data())) + break; + switch (res) { + case KEY_DOWN: + menu_driver(curses_menu, REQ_DOWN_ITEM); + break; + case KEY_UP: + menu_driver(curses_menu, REQ_UP_ITEM); + break; + case KEY_NPAGE: + menu_driver(curses_menu, REQ_SCR_DPAGE); + break; + case KEY_PPAGE: + menu_driver(curses_menu, REQ_SCR_UPAGE); + break; + case KEY_HOME: + menu_driver(curses_menu, REQ_FIRST_ITEM); + break; + case KEY_END: + menu_driver(curses_menu, REQ_LAST_ITEM); + break; + case 'h': + case '?': + show_help((struct menu *) item_data()); + break; + } + if (res == 10 || res == 27 || res == ' ' || + res == KEY_LEFT){ + break; + } + refresh_all_windows(main_window); + } + /* if ESC or left */ + if (res == 27 || res == KEY_LEFT) + break; + + child = item_data(); + if (!child || !menu_is_visible(child) || !child->sym) + continue; + switch (res) { + case ' ': + case 10: + case KEY_RIGHT: + sym_set_tristate_value(child->sym, yes); + return; + case 'h': + case '?': + show_help(child); + active = child->sym; + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_string(struct menu *menu) +{ + const char *prompt = menu_get_prompt(menu); + + while (1) { + int res; + const char *heading; + + switch (sym_get_type(menu->sym)) { + case S_INT: + heading = _(inputbox_instructions_int); + break; + case S_HEX: + heading = _(inputbox_instructions_hex); + break; + case S_STRING: + heading = _(inputbox_instructions_string); + break; + default: + heading = _("Internal nconf error!"); + } + res = dialog_inputbox(main_window, + prompt ? _(prompt) : _("Main Menu"), + heading, + sym_get_string_value(menu->sym), + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (sym_set_string_value(menu->sym, + dialog_input_result)) + return; + btn_dialog(main_window, + _("You have made an invalid entry."), 0); + break; + case 1: + show_help(menu); + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_load(void) +{ + while (1) { + int res; + res = dialog_inputbox(main_window, + NULL, load_config_text, + filename, + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (!dialog_input_result[0]) + return; + if (!conf_read(dialog_input_result)) { + set_config_filename(dialog_input_result); + sym_set_change_count(1); + return; + } + btn_dialog(main_window, _("File does not exist!"), 0); + break; + case 1: + show_scroll_win(main_window, + _("Load Alternate Configuration"), + load_config_help); + break; + case KEY_EXIT: + return; + } + } +} + +static void conf_save(void) +{ + while (1) { + int res; + res = dialog_inputbox(main_window, + NULL, save_config_text, + filename, + &dialog_input_result, + &dialog_input_result_len); + switch (res) { + case 0: + if (!dialog_input_result[0]) + return; + res = conf_write(dialog_input_result); + if (!res) { + set_config_filename(dialog_input_result); + return; + } + btn_dialog(main_window, _("Can't create file! " + "Probably a nonexistent directory."), + 1, ""); + break; + case 1: + show_scroll_win(main_window, + _("Save Alternate Configuration"), + save_config_help); + break; + case KEY_EXIT: + return; + } + } +} + +static void setup_windows(void) +{ + int lines, columns; + + getmaxyx(stdscr, lines, columns); + + if (main_window != NULL) + delwin(main_window); + + /* set up the menu and menu window */ + main_window = newwin(lines-2, columns-2, 2, 1); + keypad(main_window, TRUE); + mwin_max_lines = lines-7; + mwin_max_cols = columns-6; + + /* panels order is from bottom to top */ + new_panel(main_window); +} + +int main(int ac, char **av) +{ + int lines, columns; + char *mode; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + if (ac > 1 && strcmp(av[1], "-s") == 0) { + /* Silence conf_read() until the real callback is set up */ + conf_set_message_callback(NULL); + av++; + } + conf_parse(av[1]); + conf_read(NULL); + + mode = getenv("NCONFIG_MODE"); + if (mode) { + if (!strcasecmp(mode, "single_menu")) + single_menu_mode = 1; + } + + /* Initialize curses */ + initscr(); + /* set color theme */ + set_colors(); + + cbreak(); + noecho(); + keypad(stdscr, TRUE); + curs_set(0); + + getmaxyx(stdscr, lines, columns); + if (columns < 75 || lines < 20) { + endwin(); + printf("Your terminal should have at " + "least 20 lines and 75 columns\n"); + return 1; + } + + notimeout(stdscr, FALSE); +#if NCURSES_REENTRANT + set_escdelay(1); +#else + ESCDELAY = 1; +#endif + + /* set btns menu */ + curses_menu = new_menu(curses_menu_items); + menu_opts_off(curses_menu, O_SHOWDESC); + menu_opts_on(curses_menu, O_SHOWMATCH); + menu_opts_on(curses_menu, O_ONEVALUE); + menu_opts_on(curses_menu, O_NONCYCLIC); + menu_opts_on(curses_menu, O_IGNORECASE); + set_menu_mark(curses_menu, " "); + set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]); + set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]); + set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]); + + set_config_filename(conf_get_configname()); + setup_windows(); + + /* check for KEY_FUNC(1) */ + if (has_key(KEY_F(1)) == FALSE) { + show_scroll_win(main_window, + _("Instructions"), + _(menu_no_f_instructions)); + } + + conf_set_message_callback(conf_message_callback); + /* do the work */ + while (!global_exit) { + conf(&rootmenu); + if (!global_exit && do_exit() == 0) + break; + } + /* ok, we are done */ + unpost_menu(curses_menu); + free_menu(curses_menu); + delwin(main_window); + clear(); + refresh(); + endwin(); + return 0; +} diff --git a/tools/kconfig/nconf.gui.c b/tools/kconfig/nconf.gui.c new file mode 100644 index 0000000..a64b1c3 --- /dev/null +++ b/tools/kconfig/nconf.gui.c @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2008 Nir Tzachar 0) + win_rows = msg_lines+4; + else + win_rows = msg_lines+2; + + win = newwin(win_rows, total_width+4, y, x); + keypad(win, TRUE); + menu_win = derwin(win, 1, btns_width, win_rows-2, + 1+(total_width+2-btns_width)/2); + menu = new_menu(btns); + msg_win = derwin(win, win_rows-2, msg_width, 1, + 1+(total_width+2-msg_width)/2); + + set_menu_fore(menu, attributes[DIALOG_MENU_FORE]); + set_menu_back(menu, attributes[DIALOG_MENU_BACK]); + + (void) wattrset(win, attributes[DIALOG_BOX]); + box(win, 0, 0); + + /* print message */ + (void) wattrset(msg_win, attributes[DIALOG_TEXT]); + fill_window(msg_win, msg); + + set_menu_win(menu, win); + set_menu_sub(menu, menu_win); + set_menu_format(menu, 1, btn_num); + menu_opts_off(menu, O_SHOWDESC); + menu_opts_off(menu, O_SHOWMATCH); + menu_opts_on(menu, O_ONEVALUE); + menu_opts_on(menu, O_NONCYCLIC); + set_menu_mark(menu, ""); + post_menu(menu); + + + touchwin(win); + refresh_all_windows(main_window); + while ((res = wgetch(win))) { + switch (res) { + case KEY_LEFT: + menu_driver(menu, REQ_LEFT_ITEM); + break; + case KEY_RIGHT: + menu_driver(menu, REQ_RIGHT_ITEM); + break; + case 10: /* ENTER */ + case 27: /* ESCAPE */ + case ' ': + case KEY_F(F_BACK): + case KEY_F(F_EXIT): + break; + } + touchwin(win); + refresh_all_windows(main_window); + + if (res == 10 || res == ' ') { + res = item_index(current_item(menu)); + break; + } else if (res == 27 || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) { + res = KEY_EXIT; + break; + } + } + + unpost_menu(menu); + free_menu(menu); + for (i = 0; i < btn_num; i++) + free_item(btns[i]); + + delwin(win); + return res; +} + +int dialog_inputbox(WINDOW *main_window, + const char *title, const char *prompt, + const char *init, char **resultp, int *result_len) +{ + int prompt_lines = 0; + int prompt_width = 0; + WINDOW *win; + WINDOW *prompt_win; + WINDOW *form_win; + PANEL *panel; + int i, x, y, lines, columns, win_lines, win_cols; + int res = -1; + int cursor_position = strlen(init); + int cursor_form_win; + char *result = *resultp; + + getmaxyx(stdscr, lines, columns); + + if (strlen(init)+1 > *result_len) { + *result_len = strlen(init)+1; + *resultp = result = realloc(result, *result_len); + } + + /* find the widest line of msg: */ + prompt_lines = get_line_no(prompt); + for (i = 0; i < prompt_lines; i++) { + const char *line = get_line(prompt, i); + int len = get_line_length(line); + prompt_width = max(prompt_width, len); + } + + if (title) + prompt_width = max(prompt_width, strlen(title)); + + win_lines = min(prompt_lines+6, lines-2); + win_cols = min(prompt_width+7, columns-2); + prompt_lines = max(win_lines-6, 0); + prompt_width = max(win_cols-7, 0); + + /* place dialog in middle of screen */ + y = (lines-win_lines)/2; + x = (columns-win_cols)/2; + + strncpy(result, init, *result_len); + + /* create the windows */ + win = newwin(win_lines, win_cols, y, x); + prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2); + form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2); + keypad(form_win, TRUE); + + (void) wattrset(form_win, attributes[INPUT_FIELD]); + + (void) wattrset(win, attributes[INPUT_BOX]); + box(win, 0, 0); + (void) wattrset(win, attributes[INPUT_HEADING]); + if (title) + mvwprintw(win, 0, 3, "%s", title); + + /* print message */ + (void) wattrset(prompt_win, attributes[INPUT_TEXT]); + fill_window(prompt_win, prompt); + + mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); + cursor_form_win = min(cursor_position, prompt_width-1); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); + + /* create panels */ + panel = new_panel(win); + + /* show the cursor */ + curs_set(1); + + touchwin(win); + refresh_all_windows(main_window); + while ((res = wgetch(form_win))) { + int len = strlen(result); + switch (res) { + case 10: /* ENTER */ + case 27: /* ESCAPE */ + case KEY_F(F_HELP): + case KEY_F(F_EXIT): + case KEY_F(F_BACK): + break; + case 127: + case KEY_BACKSPACE: + if (cursor_position > 0) { + memmove(&result[cursor_position-1], + &result[cursor_position], + len-cursor_position+1); + cursor_position--; + cursor_form_win--; + len--; + } + break; + case KEY_DC: + if (cursor_position >= 0 && cursor_position < len) { + memmove(&result[cursor_position], + &result[cursor_position+1], + len-cursor_position+1); + len--; + } + break; + case KEY_UP: + case KEY_RIGHT: + if (cursor_position < len) { + cursor_position++; + cursor_form_win++; + } + break; + case KEY_DOWN: + case KEY_LEFT: + if (cursor_position > 0) { + cursor_position--; + cursor_form_win--; + } + break; + case KEY_HOME: + cursor_position = 0; + cursor_form_win = 0; + break; + case KEY_END: + cursor_position = len; + cursor_form_win = min(cursor_position, prompt_width-1); + break; + default: + if ((isgraph(res) || isspace(res))) { + /* one for new char, one for '\0' */ + if (len+2 > *result_len) { + *result_len = len+2; + *resultp = result = realloc(result, + *result_len); + } + /* insert the char at the proper position */ + memmove(&result[cursor_position+1], + &result[cursor_position], + len-cursor_position+1); + result[cursor_position] = res; + cursor_position++; + cursor_form_win++; + len++; + } else { + mvprintw(0, 0, "unknown key: %d\n", res); + } + break; + } + if (cursor_form_win < 0) + cursor_form_win = 0; + else if (cursor_form_win > prompt_width-1) + cursor_form_win = prompt_width-1; + + wmove(form_win, 0, 0); + wclrtoeol(form_win); + mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); + wmove(form_win, 0, cursor_form_win); + touchwin(win); + refresh_all_windows(main_window); + + if (res == 10) { + res = 0; + break; + } else if (res == 27 || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) { + res = KEY_EXIT; + break; + } else if (res == KEY_F(F_HELP)) { + res = 1; + break; + } + } + + /* hide the cursor */ + curs_set(0); + del_panel(panel); + delwin(prompt_win); + delwin(form_win); + delwin(win); + return res; +} + +/* refresh all windows in the correct order */ +void refresh_all_windows(WINDOW *main_window) +{ + update_panels(); + touchwin(main_window); + refresh(); +} + +/* layman's scrollable window... */ +void show_scroll_win(WINDOW *main_window, + const char *title, + const char *text) +{ + int res; + int total_lines = get_line_no(text); + int x, y, lines, columns; + int start_x = 0, start_y = 0; + int text_lines = 0, text_cols = 0; + int total_cols = 0; + int win_cols = 0; + int win_lines = 0; + int i = 0; + WINDOW *win; + WINDOW *pad; + PANEL *panel; + + getmaxyx(stdscr, lines, columns); + + /* find the widest line of msg: */ + total_lines = get_line_no(text); + for (i = 0; i < total_lines; i++) { + const char *line = get_line(text, i); + int len = get_line_length(line); + total_cols = max(total_cols, len+2); + } + + /* create the pad */ + pad = newpad(total_lines+10, total_cols+10); + (void) wattrset(pad, attributes[SCROLLWIN_TEXT]); + fill_window(pad, text); + + win_lines = min(total_lines+4, lines-2); + win_cols = min(total_cols+2, columns-2); + text_lines = max(win_lines-4, 0); + text_cols = max(win_cols-2, 0); + + /* place window in middle of screen */ + y = (lines-win_lines)/2; + x = (columns-win_cols)/2; + + win = newwin(win_lines, win_cols, y, x); + keypad(win, TRUE); + /* show the help in the help window, and show the help panel */ + (void) wattrset(win, attributes[SCROLLWIN_BOX]); + box(win, 0, 0); + (void) wattrset(win, attributes[SCROLLWIN_HEADING]); + mvwprintw(win, 0, 3, " %s ", title); + panel = new_panel(win); + + /* handle scrolling */ + do { + + copywin(pad, win, start_y, start_x, 2, 2, text_lines, + text_cols, 0); + print_in_middle(win, + text_lines+2, + 0, + text_cols, + "", + attributes[DIALOG_MENU_FORE]); + wrefresh(win); + + res = wgetch(win); + switch (res) { + case KEY_NPAGE: + case ' ': + case 'd': + start_y += text_lines-2; + break; + case KEY_PPAGE: + case 'u': + start_y -= text_lines+2; + break; + case KEY_HOME: + start_y = 0; + break; + case KEY_END: + start_y = total_lines-text_lines; + break; + case KEY_DOWN: + case 'j': + start_y++; + break; + case KEY_UP: + case 'k': + start_y--; + break; + case KEY_LEFT: + case 'h': + start_x--; + break; + case KEY_RIGHT: + case 'l': + start_x++; + break; + } + if (res == 10 || res == 27 || res == 'q' || + res == KEY_F(F_HELP) || res == KEY_F(F_BACK) || + res == KEY_F(F_EXIT)) + break; + if (start_y < 0) + start_y = 0; + if (start_y >= total_lines-text_lines) + start_y = total_lines-text_lines; + if (start_x < 0) + start_x = 0; + if (start_x >= total_cols-text_cols) + start_x = total_cols-text_cols; + } while (res); + + del_panel(panel); + delwin(win); + refresh_all_windows(main_window); +} diff --git a/tools/kconfig/nconf.h b/tools/kconfig/nconf.h new file mode 100644 index 0000000..0d52617 --- /dev/null +++ b/tools/kconfig/nconf.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Nir Tzachar +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ncurses.h" + +#define max(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a > _b ? _a : _b; }) + +#define min(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a < _b ? _a : _b; }) + +typedef enum { + NORMAL = 1, + MAIN_HEADING, + MAIN_MENU_BOX, + MAIN_MENU_FORE, + MAIN_MENU_BACK, + MAIN_MENU_GREY, + MAIN_MENU_HEADING, + SCROLLWIN_TEXT, + SCROLLWIN_HEADING, + SCROLLWIN_BOX, + DIALOG_TEXT, + DIALOG_MENU_FORE, + DIALOG_MENU_BACK, + DIALOG_BOX, + INPUT_BOX, + INPUT_HEADING, + INPUT_TEXT, + INPUT_FIELD, + FUNCTION_TEXT, + FUNCTION_HIGHLIGHT, + ATTR_MAX +} attributes_t; +extern attributes_t attributes[]; + +typedef enum { + F_HELP = 1, + F_SYMBOL = 2, + F_INSTS = 3, + F_CONF = 4, + F_BACK = 5, + F_SAVE = 6, + F_LOAD = 7, + F_SEARCH = 8, + F_EXIT = 9, +} function_key; + +void set_colors(void); + +/* this changes the windows attributes !!! */ +void print_in_middle(WINDOW *win, + int starty, + int startx, + int width, + const char *string, + chtype color); +int get_line_length(const char *line); +int get_line_no(const char *text); +const char *get_line(const char *text, int line_no); +void fill_window(WINDOW *win, const char *text); +int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...); +int dialog_inputbox(WINDOW *main_window, + const char *title, const char *prompt, + const char *init, char **resultp, int *result_len); +void refresh_all_windows(WINDOW *main_window); +void show_scroll_win(WINDOW *main_window, + const char *title, + const char *text); diff --git a/tools/kconfig/qconf.cc b/tools/kconfig/qconf.cc new file mode 100644 index 0000000..ae6c725 --- /dev/null +++ b/tools/kconfig/qconf.cc @@ -0,0 +1,1879 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Copyright (C) 2015 Boris Barbulovski + * Released under the terms of the GNU GPL v2.0. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lkc.h" +#include "qconf.h" + +#include "qconf.moc" +#include "images.c" + +#ifdef _ +# undef _ +# define _ qgettext +#endif + +static QApplication *configApp; +static ConfigSettings *configSettings; + +QAction *ConfigMainWindow::saveAction; + +static inline QString qgettext(const char* str) +{ + return QString::fromLocal8Bit(gettext(str)); +} + +static inline QString qgettext(const QString& str) +{ + return QString::fromLocal8Bit(gettext(str.toLatin1())); +} + +ConfigSettings::ConfigSettings() + : QSettings("kernel.org", "qconf") +{ +} + +/** + * Reads a list of integer values from the application settings. + */ +QList ConfigSettings::readSizes(const QString& key, bool *ok) +{ + QList result; + + if (contains(key)) + { + QStringList entryList = value(key).toStringList(); + QStringList::Iterator it; + + for (it = entryList.begin(); it != entryList.end(); ++it) + result.push_back((*it).toInt()); + + *ok = true; + } + else + *ok = false; + + return result; +} + +/** + * Writes a list of integer values to the application settings. + */ +bool ConfigSettings::writeSizes(const QString& key, const QList& value) +{ + QStringList stringList; + QList::ConstIterator it; + + for (it = value.begin(); it != value.end(); ++it) + stringList.push_back(QString::number(*it)); + setValue(key, stringList); + + return true; +} + + +/* + * set the new data + * TODO check the value + */ +void ConfigItem::okRename(int col) +{ +} + +/* + * update the displayed of a menu entry + */ +void ConfigItem::updateMenu(void) +{ + ConfigList* list; + struct symbol* sym; + struct property *prop; + QString prompt; + int type; + tristate expr; + + list = listView(); + if (goParent) { + setPixmap(promptColIdx, list->menuBackPix); + prompt = ".."; + goto set_prompt; + } + + sym = menu->sym; + prop = menu->prompt; + prompt = _(menu_get_prompt(menu)); + + if (prop) switch (prop->type) { + case P_MENU: + if (list->mode == singleMode || list->mode == symbolMode) { + /* a menuconfig entry is displayed differently + * depending whether it's at the view root or a child. + */ + if (sym && list->rootEntry == menu) + break; + setPixmap(promptColIdx, list->menuPix); + } else { + if (sym) + break; + setPixmap(promptColIdx, QIcon()); + } + goto set_prompt; + case P_COMMENT: + setPixmap(promptColIdx, QIcon()); + goto set_prompt; + default: + ; + } + if (!sym) + goto set_prompt; + + setText(nameColIdx, QString::fromLocal8Bit(sym->name)); + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + char ch; + + if (!sym_is_changable(sym) && list->optMode == normalOpt) { + setPixmap(promptColIdx, QIcon()); + setText(noColIdx, QString::null); + setText(modColIdx, QString::null); + setText(yesColIdx, QString::null); + break; + } + expr = sym_get_tristate_value(sym); + switch (expr) { + case yes: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceYesPix); + else + setPixmap(promptColIdx, list->symbolYesPix); + setText(yesColIdx, "Y"); + ch = 'Y'; + break; + case mod: + setPixmap(promptColIdx, list->symbolModPix); + setText(modColIdx, "M"); + ch = 'M'; + break; + default: + if (sym_is_choice_value(sym) && type == S_BOOLEAN) + setPixmap(promptColIdx, list->choiceNoPix); + else + setPixmap(promptColIdx, list->symbolNoPix); + setText(noColIdx, "N"); + ch = 'N'; + break; + } + if (expr != no) + setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0); + if (expr != mod) + setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0); + if (expr != yes) + setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0); + + setText(dataColIdx, QChar(ch)); + break; + case S_INT: + case S_HEX: + case S_STRING: + const char* data; + + data = sym_get_string_value(sym); + + setText(dataColIdx, data); + if (type == S_STRING) + prompt = QString("%1: %2").arg(prompt).arg(data); + else + prompt = QString("(%2) %1").arg(prompt).arg(data); + break; + } + if (!sym_has_value(sym) && visible) + prompt += _(" (NEW)"); +set_prompt: + setText(promptColIdx, prompt); +} + +void ConfigItem::testUpdateMenu(bool v) +{ + ConfigItem* i; + + visible = v; + if (!menu) + return; + + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { + /* the menu entry changed, so update all list items */ + menu->flags &= ~MENU_CHANGED; + for (i = (ConfigItem*)menu->data; i; i = i->nextItem) + i->updateMenu(); + } else if (listView()->updateAll) + updateMenu(); +} + + +/* + * construct a menu entry + */ +void ConfigItem::init(void) +{ + if (menu) { + ConfigList* list = listView(); + nextItem = (ConfigItem*)menu->data; + menu->data = this; + + if (list->mode != fullMode) + setExpanded(true); + sym_calc_value(menu->sym); + } + updateMenu(); +} + +/* + * destruct a menu entry + */ +ConfigItem::~ConfigItem(void) +{ + if (menu) { + ConfigItem** ip = (ConfigItem**)&menu->data; + for (; *ip; ip = &(*ip)->nextItem) { + if (*ip == this) { + *ip = nextItem; + break; + } + } + } +} + +ConfigLineEdit::ConfigLineEdit(ConfigView* parent) + : Parent(parent) +{ + connect(this, SIGNAL(editingFinished()), SLOT(hide())); +} + +void ConfigLineEdit::show(ConfigItem* i) +{ + item = i; + if (sym_get_string_value(item->menu->sym)) + setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym))); + else + setText(QString::null); + Parent::show(); + setFocus(); +} + +void ConfigLineEdit::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) { + case Qt::Key_Escape: + break; + case Qt::Key_Return: + case Qt::Key_Enter: + sym_set_string_value(item->menu->sym, text().toLatin1()); + parent()->updateList(item); + break; + default: + Parent::keyPressEvent(e); + return; + } + e->accept(); + parent()->list->setFocus(); + hide(); +} + +ConfigList::ConfigList(ConfigView* p, const char *name) + : Parent(p), + updateAll(false), + symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no), + choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no), + menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void), + showName(false), showRange(false), showData(false), mode(singleMode), optMode(normalOpt), + rootEntry(0), headerPopup(0) +{ + int i; + + setObjectName(name); + setSortingEnabled(false); + setRootIsDecorated(true); + + setVerticalScrollMode(ScrollPerPixel); + setHorizontalScrollMode(ScrollPerPixel); + + setHeaderLabels(QStringList() << _("Option") << _("Name") << "N" << "M" << "Y" << _("Value")); + + connect(this, SIGNAL(itemSelectionChanged(void)), + SLOT(updateSelection(void))); + + if (name) { + configSettings->beginGroup(name); + showName = configSettings->value("/showName", false).toBool(); + showRange = configSettings->value("/showRange", false).toBool(); + showData = configSettings->value("/showData", false).toBool(); + optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt(); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } + + addColumn(promptColIdx); + + reinit(); +} + +bool ConfigList::menuSkip(struct menu *menu) +{ + if (optMode == normalOpt && menu_is_visible(menu)) + return false; + if (optMode == promptOpt && menu_has_prompt(menu)) + return false; + if (optMode == allOpt) + return false; + return true; +} + +void ConfigList::reinit(void) +{ + removeColumn(dataColIdx); + removeColumn(yesColIdx); + removeColumn(modColIdx); + removeColumn(noColIdx); + removeColumn(nameColIdx); + + if (showName) + addColumn(nameColIdx); + if (showRange) { + addColumn(noColIdx); + addColumn(modColIdx); + addColumn(yesColIdx); + } + if (showData) + addColumn(dataColIdx); + + updateListAll(); +} + +void ConfigList::saveSettings(void) +{ + if (!objectName().isEmpty()) { + configSettings->beginGroup(objectName()); + configSettings->setValue("/showName", showName); + configSettings->setValue("/showRange", showRange); + configSettings->setValue("/showData", showData); + configSettings->setValue("/optionMode", (int)optMode); + configSettings->endGroup(); + } +} + +ConfigItem* ConfigList::findConfigItem(struct menu *menu) +{ + ConfigItem* item = (ConfigItem*)menu->data; + + for (; item; item = item->nextItem) { + if (this == item->listView()) + break; + } + + return item; +} + +void ConfigList::updateSelection(void) +{ + struct menu *menu; + enum prop_type type; + + if (selectedItems().count() == 0) + return; + + ConfigItem* item = (ConfigItem*)selectedItems().first(); + if (!item) + return; + + menu = item->menu; + emit menuChanged(menu); + if (!menu) + return; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (mode == menuMode && type == P_MENU) + emit menuSelected(menu); +} + +void ConfigList::updateList(ConfigItem* item) +{ + ConfigItem* last = 0; + + if (!rootEntry) { + if (mode != listMode) + goto update; + QTreeWidgetItemIterator it(this); + ConfigItem* item; + + while (*it) { + item = (ConfigItem*)(*it); + if (!item->menu) + continue; + item->testUpdateMenu(menu_is_visible(item->menu)); + + ++it; + } + return; + } + + if (rootEntry != &rootmenu && (mode == singleMode || + (mode == symbolMode && rootEntry->parent != &rootmenu))) { + item = (ConfigItem *)topLevelItem(0); + if (!item) + item = new ConfigItem(this, 0, true); + last = item; + } + if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) && + rootEntry->sym && rootEntry->prompt) { + item = last ? last->nextSibling() : firstChild(); + if (!item) + item = new ConfigItem(this, last, rootEntry, true); + else + item->testUpdateMenu(true); + + updateMenuList(item, rootEntry); + update(); + resizeColumnToContents(0); + return; + } +update: + updateMenuList(this, rootEntry); + update(); + resizeColumnToContents(0); +} + +void ConfigList::setValue(ConfigItem* item, tristate val) +{ + struct symbol* sym; + int type; + tristate oldval; + + sym = item->menu ? item->menu->sym : 0; + if (!sym) + return; + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldval = sym_get_tristate_value(sym); + + if (!sym_set_tristate_value(sym, val)) + return; + if (oldval == no && item->menu->list) + item->setExpanded(true); + parent()->updateList(item); + break; + } +} + +void ConfigList::changeValue(ConfigItem* item) +{ + struct symbol* sym; + struct menu* menu; + int type, oldexpr, newexpr; + + menu = item->menu; + if (!menu) + return; + sym = menu->sym; + if (!sym) { + if (item->menu->list) + item->setExpanded(!item->isExpanded()); + return; + } + + type = sym_get_type(sym); + switch (type) { + case S_BOOLEAN: + case S_TRISTATE: + oldexpr = sym_get_tristate_value(sym); + newexpr = sym_toggle_tristate_value(sym); + if (item->menu->list) { + if (oldexpr == newexpr) + item->setExpanded(!item->isExpanded()); + else if (oldexpr == no) + item->setExpanded(true); + } + if (oldexpr != newexpr) + parent()->updateList(item); + break; + case S_INT: + case S_HEX: + case S_STRING: + parent()->lineEdit->show(item); + break; + } +} + +void ConfigList::setRootMenu(struct menu *menu) +{ + enum prop_type type; + + if (rootEntry == menu) + return; + type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type != P_MENU) + return; + updateMenuList(this, 0); + rootEntry = menu; + updateListAll(); + if (currentItem()) { + currentItem()->setSelected(hasFocus()); + scrollToItem(currentItem()); + } +} + +void ConfigList::setParentMenu(void) +{ + ConfigItem* item; + struct menu *oldroot; + + oldroot = rootEntry; + if (rootEntry == &rootmenu) + return; + setRootMenu(menu_get_parent_menu(rootEntry->parent)); + + QTreeWidgetItemIterator it(this); + while (*it) { + item = (ConfigItem *)(*it); + if (item->menu == oldroot) { + setCurrentItem(item); + scrollToItem(item); + break; + } + + ++it; + } +} + +/* + * update all the children of a menu entry + * removes/adds the entries from the parent widget as necessary + * + * parent: either the menu list widget or a menu entry widget + * menu: entry to be updated + */ +void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu) +{ + struct menu* child; + ConfigItem* item; + ConfigItem* last; + bool visible; + enum prop_type type; + + if (!menu) { + while (parent->childCount() > 0) + { + delete parent->takeChild(0); + } + + return; + } + + last = parent->firstChild(); + if (last && !last->goParent) + last = 0; + for (child = menu->list; child; child = child->next) { + item = last ? last->nextSibling() : parent->firstChild(); + type = child->prompt ? child->prompt->type : P_UNKNOWN; + + switch (mode) { + case menuMode: + if (!(child->flags & MENU_ROOT)) + goto hide; + break; + case symbolMode: + if (child->flags & MENU_ROOT) + goto hide; + break; + default: + break; + } + + visible = menu_is_visible(child); + if (!menuSkip(child)) { + if (!child->sym && !child->list && !child->prompt) + continue; + if (!item || item->menu != child) + item = new ConfigItem(parent, last, child, visible); + else + item->testUpdateMenu(visible); + + if (mode == fullMode || mode == menuMode || type != P_MENU) + updateMenuList(item, child); + else + updateMenuList(item, 0); + last = item; + continue; + } + hide: + if (item && item->menu == child) { + last = parent->firstChild(); + if (last == item) + last = 0; + else while (last->nextSibling() != item) + last = last->nextSibling(); + delete item; + } + } +} + +void ConfigList::updateMenuList(ConfigList *parent, struct menu* menu) +{ + struct menu* child; + ConfigItem* item; + ConfigItem* last; + bool visible; + enum prop_type type; + + if (!menu) { + while (parent->topLevelItemCount() > 0) + { + delete parent->takeTopLevelItem(0); + } + + return; + } + + last = (ConfigItem*)parent->topLevelItem(0); + if (last && !last->goParent) + last = 0; + for (child = menu->list; child; child = child->next) { + item = last ? last->nextSibling() : (ConfigItem*)parent->topLevelItem(0); + type = child->prompt ? child->prompt->type : P_UNKNOWN; + + switch (mode) { + case menuMode: + if (!(child->flags & MENU_ROOT)) + goto hide; + break; + case symbolMode: + if (child->flags & MENU_ROOT) + goto hide; + break; + default: + break; + } + + visible = menu_is_visible(child); + if (!menuSkip(child)) { + if (!child->sym && !child->list && !child->prompt) + continue; + if (!item || item->menu != child) + item = new ConfigItem(parent, last, child, visible); + else + item->testUpdateMenu(visible); + + if (mode == fullMode || mode == menuMode || type != P_MENU) + updateMenuList(item, child); + else + updateMenuList(item, 0); + last = item; + continue; + } + hide: + if (item && item->menu == child) { + last = (ConfigItem*)parent->topLevelItem(0); + if (last == item) + last = 0; + else while (last->nextSibling() != item) + last = last->nextSibling(); + delete item; + } + } +} + +void ConfigList::keyPressEvent(QKeyEvent* ev) +{ + QTreeWidgetItem* i = currentItem(); + ConfigItem* item; + struct menu *menu; + enum prop_type type; + + if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) { + emit parentSelected(); + ev->accept(); + return; + } + + if (!i) { + Parent::keyPressEvent(ev); + return; + } + item = (ConfigItem*)i; + + switch (ev->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (item->goParent) { + emit parentSelected(); + break; + } + menu = item->menu; + if (!menu) + break; + type = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (type == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) { + emit menuSelected(menu); + break; + } + case Qt::Key_Space: + changeValue(item); + break; + case Qt::Key_N: + setValue(item, no); + break; + case Qt::Key_M: + setValue(item, mod); + break; + case Qt::Key_Y: + setValue(item, yes); + break; + default: + Parent::keyPressEvent(ev); + return; + } + ev->accept(); +} + +void ConfigList::mousePressEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y()); + Parent::mousePressEvent(e); +} + +void ConfigList::mouseReleaseEvent(QMouseEvent* e) +{ + QPoint p = e->pos(); + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + QIcon icon; + int idx, x; + + if (!item) + goto skip; + + menu = item->menu; + x = header()->offset() + p.x(); + idx = header()->logicalIndexAt(x); + switch (idx) { + case promptColIdx: + icon = item->pixmap(promptColIdx); + if (!icon.isNull()) { + int off = header()->sectionPosition(0) + visualRect(indexAt(p)).x() + 4; // 4 is Hardcoded image offset. There might be a way to do it properly. + if (x >= off && x < off + icon.availableSizes().first().width()) { + if (item->goParent) { + emit parentSelected(); + break; + } else if (!menu) + break; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && rootEntry != menu && + mode != fullMode && mode != menuMode) + emit menuSelected(menu); + else + changeValue(item); + } + } + break; + case noColIdx: + setValue(item, no); + break; + case modColIdx: + setValue(item, mod); + break; + case yesColIdx: + setValue(item, yes); + break; + case dataColIdx: + changeValue(item); + break; + } + +skip: + //printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y()); + Parent::mouseReleaseEvent(e); +} + +void ConfigList::mouseMoveEvent(QMouseEvent* e) +{ + //QPoint p(contentsToViewport(e->pos())); + //printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y()); + Parent::mouseMoveEvent(e); +} + +void ConfigList::mouseDoubleClickEvent(QMouseEvent* e) +{ + QPoint p = e->pos(); // TODO: Check if this works(was contentsToViewport). + ConfigItem* item = (ConfigItem*)itemAt(p); + struct menu *menu; + enum prop_type ptype; + + if (!item) + goto skip; + if (item->goParent) { + emit parentSelected(); + goto skip; + } + menu = item->menu; + if (!menu) + goto skip; + ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; + if (ptype == P_MENU && (mode == singleMode || mode == symbolMode)) + emit menuSelected(menu); + else if (menu->sym) + changeValue(item); + +skip: + //printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y()); + Parent::mouseDoubleClickEvent(e); +} + +void ConfigList::focusInEvent(QFocusEvent *e) +{ + struct menu *menu = NULL; + + Parent::focusInEvent(e); + + ConfigItem* item = (ConfigItem *)currentItem(); + if (item) { + item->setSelected(true); + menu = item->menu; + } + emit gotFocus(menu); +} + +void ConfigList::contextMenuEvent(QContextMenuEvent *e) +{ + if (e->y() <= header()->geometry().bottom()) { + if (!headerPopup) { + QAction *action; + + headerPopup = new QMenu(this); + action = new QAction(_("Show Name"), this); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowName(bool))); + connect(parent(), SIGNAL(showNameChanged(bool)), + action, SLOT(setOn(bool))); + action->setChecked(showName); + headerPopup->addAction(action); + action = new QAction(_("Show Range"), this); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowRange(bool))); + connect(parent(), SIGNAL(showRangeChanged(bool)), + action, SLOT(setOn(bool))); + action->setChecked(showRange); + headerPopup->addAction(action); + action = new QAction(_("Show Data"), this); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowData(bool))); + connect(parent(), SIGNAL(showDataChanged(bool)), + action, SLOT(setOn(bool))); + action->setChecked(showData); + headerPopup->addAction(action); + } + headerPopup->exec(e->globalPos()); + e->accept(); + } else + e->ignore(); +} + +ConfigView*ConfigView::viewList; +QAction *ConfigView::showNormalAction; +QAction *ConfigView::showAllAction; +QAction *ConfigView::showPromptAction; + +ConfigView::ConfigView(QWidget* parent, const char *name) + : Parent(parent) +{ + setObjectName(name); + QVBoxLayout *verticalLayout = new QVBoxLayout(this); + verticalLayout->setContentsMargins(0, 0, 0, 0); + + list = new ConfigList(this); + verticalLayout->addWidget(list); + lineEdit = new ConfigLineEdit(this); + lineEdit->hide(); + verticalLayout->addWidget(lineEdit); + + this->nextView = viewList; + viewList = this; +} + +ConfigView::~ConfigView(void) +{ + ConfigView** vp; + + for (vp = &viewList; *vp; vp = &(*vp)->nextView) { + if (*vp == this) { + *vp = nextView; + break; + } + } +} + +void ConfigView::setOptionMode(QAction *act) +{ + if (act == showNormalAction) + list->optMode = normalOpt; + else if (act == showAllAction) + list->optMode = allOpt; + else + list->optMode = promptOpt; + + list->updateListAll(); +} + +void ConfigView::setShowName(bool b) +{ + if (list->showName != b) { + list->showName = b; + list->reinit(); + emit showNameChanged(b); + } +} + +void ConfigView::setShowRange(bool b) +{ + if (list->showRange != b) { + list->showRange = b; + list->reinit(); + emit showRangeChanged(b); + } +} + +void ConfigView::setShowData(bool b) +{ + if (list->showData != b) { + list->showData = b; + list->reinit(); + emit showDataChanged(b); + } +} + +void ConfigList::setAllOpen(bool open) +{ + QTreeWidgetItemIterator it(this); + + while (*it) { + (*it)->setExpanded(open); + + ++it; + } +} + +void ConfigView::updateList(ConfigItem* item) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateList(item); +} + +void ConfigView::updateListAll(void) +{ + ConfigView* v; + + for (v = viewList; v; v = v->nextView) + v->list->updateListAll(); +} + +ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name) + : Parent(parent), sym(0), _menu(0) +{ + setObjectName(name); + + + if (!objectName().isEmpty()) { + configSettings->beginGroup(objectName()); + setShowDebug(configSettings->value("/showDebug", false).toBool()); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigInfoView::saveSettings(void) +{ + if (!objectName().isEmpty()) { + configSettings->beginGroup(objectName()); + configSettings->setValue("/showDebug", showDebug()); + configSettings->endGroup(); + } +} + +void ConfigInfoView::setShowDebug(bool b) +{ + if (_showDebug != b) { + _showDebug = b; + if (_menu) + menuInfo(); + else if (sym) + symbolInfo(); + emit showDebugChanged(b); + } +} + +void ConfigInfoView::setInfo(struct menu *m) +{ + if (_menu == m) + return; + _menu = m; + sym = NULL; + if (!_menu) + clear(); + else + menuInfo(); +} + +void ConfigInfoView::symbolInfo(void) +{ + QString str; + + str += "Symbol: "; + str += print_filter(sym->name); + str += "

value: "; + str += print_filter(sym_get_string_value(sym)); + str += "
visibility: "; + str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n"; + str += "
"; + str += debug_info(sym); + + setText(str); +} + +void ConfigInfoView::menuInfo(void) +{ + struct symbol* sym; + QString head, debug, help; + + sym = _menu->sym; + if (sym) { + if (_menu->prompt) { + head += ""; + head += print_filter(_(_menu->prompt->text)); + head += ""; + if (sym->name) { + head += " ("; + if (showDebug()) + head += QString().sprintf("", sym); + head += print_filter(sym->name); + if (showDebug()) + head += ""; + head += ")"; + } + } else if (sym->name) { + head += ""; + if (showDebug()) + head += QString().sprintf("", sym); + head += print_filter(sym->name); + if (showDebug()) + head += ""; + head += ""; + } + head += "

"; + + if (showDebug()) + debug = debug_info(sym); + + struct gstr help_gstr = str_new(); + menu_get_ext_help(_menu, &help_gstr); + help = print_filter(str_get(&help_gstr)); + str_free(&help_gstr); + } else if (_menu->prompt) { + head += ""; + head += print_filter(_(_menu->prompt->text)); + head += "

"; + if (showDebug()) { + if (_menu->prompt->visible.expr) { + debug += "  dep: "; + expr_print(_menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); + debug += "

"; + } + } + } + if (showDebug()) + debug += QString().sprintf("defined at %s:%d

", _menu->file->name, _menu->lineno); + + setText(head + debug + help); +} + +QString ConfigInfoView::debug_info(struct symbol *sym) +{ + QString debug; + + debug += "type: "; + debug += print_filter(sym_type_name(sym->type)); + if (sym_is_choice(sym)) + debug += " (choice)"; + debug += "
"; + if (sym->rev_dep.expr) { + debug += "reverse dep: "; + expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + for (struct property *prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_PROMPT: + case P_MENU: + debug += QString().sprintf("prompt: ", prop->menu); + debug += print_filter(_(prop->text)); + debug += "
"; + break; + case P_DEFAULT: + case P_SELECT: + case P_RANGE: + case P_ENV: + debug += prop_get_type_name(prop->type); + debug += ": "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + break; + case P_CHOICE: + if (sym_is_choice(sym)) { + debug += "choice: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + break; + default: + debug += "unknown property: "; + debug += prop_get_type_name(prop->type); + debug += "
"; + } + if (prop->visible.expr) { + debug += "    dep: "; + expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + } + debug += "
"; + + return debug; +} + +QString ConfigInfoView::print_filter(const QString &str) +{ + QRegExp re("[<>&\"\\n]"); + QString res = str; + for (int i = 0; (i = res.indexOf(re, i)) >= 0;) { + switch (res[i].toLatin1()) { + case '<': + res.replace(i, 1, "<"); + i += 4; + break; + case '>': + res.replace(i, 1, ">"); + i += 4; + break; + case '&': + res.replace(i, 1, "&"); + i += 5; + break; + case '"': + res.replace(i, 1, """); + i += 6; + break; + case '\n': + res.replace(i, 1, "
"); + i += 4; + break; + } + } + return res; +} + +void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str) +{ + QString* text = reinterpret_cast(data); + QString str2 = print_filter(str); + + if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) { + *text += QString().sprintf("", sym); + *text += str2; + *text += ""; + } else + *text += str2; +} + +QMenu* ConfigInfoView::createStandardContextMenu(const QPoint & pos) +{ + QMenu* popup = Parent::createStandardContextMenu(pos); + QAction* action = new QAction(_("Show Debug Info"), popup); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); + connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool))); + action->setChecked(showDebug()); + popup->addSeparator(); + popup->addAction(action); + return popup; +} + +void ConfigInfoView::contextMenuEvent(QContextMenuEvent *e) +{ + Parent::contextMenuEvent(e); +} + +ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow* parent, const char *name) + : Parent(parent), result(NULL) +{ + setObjectName(name); + setWindowTitle("Search Config"); + + QVBoxLayout* layout1 = new QVBoxLayout(this); + layout1->setContentsMargins(11, 11, 11, 11); + layout1->setSpacing(6); + QHBoxLayout* layout2 = new QHBoxLayout(0); + layout2->setContentsMargins(0, 0, 0, 0); + layout2->setSpacing(6); + layout2->addWidget(new QLabel(_("Find:"), this)); + editField = new QLineEdit(this); + connect(editField, SIGNAL(returnPressed()), SLOT(search())); + layout2->addWidget(editField); + searchButton = new QPushButton(_("Search"), this); + searchButton->setAutoDefault(false); + connect(searchButton, SIGNAL(clicked()), SLOT(search())); + layout2->addWidget(searchButton); + layout1->addLayout(layout2); + + split = new QSplitter(this); + split->setOrientation(Qt::Vertical); + list = new ConfigView(split, name); + list->list->mode = listMode; + info = new ConfigInfoView(split, name); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + info, SLOT(setInfo(struct menu *))); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + parent, SLOT(setMenuLink(struct menu *))); + + layout1->addWidget(split); + + if (name) { + QVariant x, y; + int width, height; + bool ok; + + configSettings->beginGroup(name); + width = configSettings->value("/window width", parent->width() / 2).toInt(); + height = configSettings->value("/window height", parent->height() / 2).toInt(); + resize(width, height); + x = configSettings->value("/window x"); + y = configSettings->value("/window y"); + if ((x.isValid())&&(y.isValid())) + move(x.toInt(), y.toInt()); + QList sizes = configSettings->readSizes("/split", &ok); + if (ok) + split->setSizes(sizes); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigSearchWindow::saveSettings(void) +{ + if (!objectName().isEmpty()) { + configSettings->beginGroup(objectName()); + configSettings->setValue("/window x", pos().x()); + configSettings->setValue("/window y", pos().y()); + configSettings->setValue("/window width", size().width()); + configSettings->setValue("/window height", size().height()); + configSettings->writeSizes("/split", split->sizes()); + configSettings->endGroup(); + } +} + +void ConfigSearchWindow::search(void) +{ + struct symbol **p; + struct property *prop; + ConfigItem *lastItem = NULL; + + free(result); + list->list->clear(); + info->clear(); + + result = sym_re_search(editField->text().toLatin1()); + if (!result) + return; + for (p = result; *p; p++) { + for_all_prompts((*p), prop) + lastItem = new ConfigItem(list->list, lastItem, prop->menu, + menu_is_visible(prop->menu)); + } +} + +/* + * Construct the complete config widget + */ +ConfigMainWindow::ConfigMainWindow(void) + : searchWindow(0) +{ + QMenuBar* menu; + bool ok = true; + QVariant x, y; + int width, height; + char title[256]; + + QDesktopWidget *d = configApp->desktop(); + snprintf(title, sizeof(title), "%s%s", + rootmenu.prompt->text, + "" + ); + setWindowTitle(title); + + width = configSettings->value("/window width", d->width() - 64).toInt(); + height = configSettings->value("/window height", d->height() - 64).toInt(); + resize(width, height); + x = configSettings->value("/window x"); + y = configSettings->value("/window y"); + if ((x.isValid())&&(y.isValid())) + move(x.toInt(), y.toInt()); + + split1 = new QSplitter(this); + split1->setOrientation(Qt::Horizontal); + setCentralWidget(split1); + + menuView = new ConfigView(split1, "menu"); + menuList = menuView->list; + + split2 = new QSplitter(split1); + split2->setOrientation(Qt::Vertical); + + // create config tree + configView = new ConfigView(split2, "config"); + configList = configView->list; + + helpText = new ConfigInfoView(split2, "help"); + + setTabOrder(configList, helpText); + configList->setFocus(); + + menu = menuBar(); + toolBar = new QToolBar("Tools", this); + addToolBar(toolBar); + + backAction = new QAction(QPixmap(xpm_back), _("Back"), this); + connect(backAction, SIGNAL(triggered(bool)), SLOT(goBack())); + backAction->setEnabled(false); + QAction *quitAction = new QAction(_("&Quit"), this); + quitAction->setShortcut(Qt::CTRL + Qt::Key_Q); + connect(quitAction, SIGNAL(triggered(bool)), SLOT(close())); + QAction *loadAction = new QAction(QPixmap(xpm_load), _("&Load"), this); + loadAction->setShortcut(Qt::CTRL + Qt::Key_L); + connect(loadAction, SIGNAL(triggered(bool)), SLOT(loadConfig())); + saveAction = new QAction(QPixmap(xpm_save), _("&Save"), this); + saveAction->setShortcut(Qt::CTRL + Qt::Key_S); + connect(saveAction, SIGNAL(triggered(bool)), SLOT(saveConfig())); + conf_set_changed_callback(conf_changed); + // Set saveAction's initial state + conf_changed(); + QAction *saveAsAction = new QAction(_("Save &As..."), this); + connect(saveAsAction, SIGNAL(triggered(bool)), SLOT(saveConfigAs())); + QAction *searchAction = new QAction(_("&Find"), this); + searchAction->setShortcut(Qt::CTRL + Qt::Key_F); + connect(searchAction, SIGNAL(triggered(bool)), SLOT(searchConfig())); + singleViewAction = new QAction(QPixmap(xpm_single_view), _("Single View"), this); + singleViewAction->setCheckable(true); + connect(singleViewAction, SIGNAL(triggered(bool)), SLOT(showSingleView())); + splitViewAction = new QAction(QPixmap(xpm_split_view), _("Split View"), this); + splitViewAction->setCheckable(true); + connect(splitViewAction, SIGNAL(triggered(bool)), SLOT(showSplitView())); + fullViewAction = new QAction(QPixmap(xpm_tree_view), _("Full View"), this); + fullViewAction->setCheckable(true); + connect(fullViewAction, SIGNAL(triggered(bool)), SLOT(showFullView())); + + QAction *showNameAction = new QAction(_("Show Name"), this); + showNameAction->setCheckable(true); + connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool))); + showNameAction->setChecked(configView->showName()); + QAction *showRangeAction = new QAction(_("Show Range"), this); + showRangeAction->setCheckable(true); + connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool))); + QAction *showDataAction = new QAction(_("Show Data"), this); + showDataAction->setCheckable(true); + connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool))); + + QActionGroup *optGroup = new QActionGroup(this); + optGroup->setExclusive(true); + connect(optGroup, SIGNAL(triggered(QAction*)), configView, + SLOT(setOptionMode(QAction *))); + connect(optGroup, SIGNAL(triggered(QAction *)), menuView, + SLOT(setOptionMode(QAction *))); + + configView->showNormalAction = new QAction(_("Show Normal Options"), optGroup); + configView->showAllAction = new QAction(_("Show All Options"), optGroup); + configView->showPromptAction = new QAction(_("Show Prompt Options"), optGroup); + configView->showNormalAction->setCheckable(true); + configView->showAllAction->setCheckable(true); + configView->showPromptAction->setCheckable(true); + + QAction *showDebugAction = new QAction( _("Show Debug Info"), this); + showDebugAction->setCheckable(true); + connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool))); + showDebugAction->setChecked(helpText->showDebug()); + + QAction *showIntroAction = new QAction( _("Introduction"), this); + connect(showIntroAction, SIGNAL(triggered(bool)), SLOT(showIntro())); + QAction *showAboutAction = new QAction( _("About"), this); + connect(showAboutAction, SIGNAL(triggered(bool)), SLOT(showAbout())); + + // init tool bar + toolBar->addAction(backAction); + toolBar->addSeparator(); + toolBar->addAction(loadAction); + toolBar->addAction(saveAction); + toolBar->addSeparator(); + toolBar->addAction(singleViewAction); + toolBar->addAction(splitViewAction); + toolBar->addAction(fullViewAction); + + // create config menu + QMenu* config = menu->addMenu(_("&File")); + config->addAction(loadAction); + config->addAction(saveAction); + config->addAction(saveAsAction); + config->addSeparator(); + config->addAction(quitAction); + + // create edit menu + QMenu* editMenu = menu->addMenu(_("&Edit")); + editMenu->addAction(searchAction); + + // create options menu + QMenu* optionMenu = menu->addMenu(_("&Option")); + optionMenu->addAction(showNameAction); + optionMenu->addAction(showRangeAction); + optionMenu->addAction(showDataAction); + optionMenu->addSeparator(); + optionMenu->addActions(optGroup->actions()); + optionMenu->addSeparator(); + optionMenu->addAction(showDebugAction); + + // create help menu + menu->addSeparator(); + QMenu* helpMenu = menu->addMenu(_("&Help")); + helpMenu->addAction(showIntroAction); + helpMenu->addAction(showAboutAction); + + connect(configList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(configList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + connect(configList, SIGNAL(parentSelected()), + SLOT(goBack())); + connect(menuList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(menuSelected(struct menu *)), + SLOT(changeMenu(struct menu *))); + + connect(configList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + SLOT(listFocusChanged(void))); + connect(helpText, SIGNAL(menuSelected(struct menu *)), + SLOT(setMenuLink(struct menu *))); + + QString listMode = configSettings->value("/listMode", "symbol").toString(); + if (listMode == "single") + showSingleView(); + else if (listMode == "full") + showFullView(); + else /*if (listMode == "split")*/ + showSplitView(); + + // UI setup done, restore splitter positions + QList sizes = configSettings->readSizes("/split1", &ok); + if (ok) + split1->setSizes(sizes); + + sizes = configSettings->readSizes("/split2", &ok); + if (ok) + split2->setSizes(sizes); +} + +void ConfigMainWindow::loadConfig(void) +{ + QString s = QFileDialog::getOpenFileName(this, "", conf_get_configname()); + if (s.isNull()) + return; + if (conf_read(QFile::encodeName(s))) + QMessageBox::information(this, "qconf", _("Unable to load configuration!")); + ConfigView::updateListAll(); +} + +bool ConfigMainWindow::saveConfig(void) +{ + if (conf_write(NULL)) { + QMessageBox::information(this, "qconf", _("Unable to save configuration!")); + return false; + } + return true; +} + +void ConfigMainWindow::saveConfigAs(void) +{ + QString s = QFileDialog::getSaveFileName(this, "", conf_get_configname()); + if (s.isNull()) + return; + saveConfig(); +} + +void ConfigMainWindow::searchConfig(void) +{ + if (!searchWindow) + searchWindow = new ConfigSearchWindow(this, "search"); + searchWindow->show(); +} + +void ConfigMainWindow::changeMenu(struct menu *menu) +{ + configList->setRootMenu(menu); + if (configList->rootEntry->parent == &rootmenu) + backAction->setEnabled(false); + else + backAction->setEnabled(true); +} + +void ConfigMainWindow::setMenuLink(struct menu *menu) +{ + struct menu *parent; + ConfigList* list = NULL; + ConfigItem* item; + + if (configList->menuSkip(menu)) + return; + + switch (configList->mode) { + case singleMode: + list = configList; + parent = menu_get_parent_menu(menu); + if (!parent) + return; + list->setRootMenu(parent); + break; + case symbolMode: + if (menu->flags & MENU_ROOT) { + configList->setRootMenu(menu); + configList->clearSelection(); + list = menuList; + } else { + list = configList; + parent = menu_get_parent_menu(menu->parent); + if (!parent) + return; + item = menuList->findConfigItem(parent); + if (item) { + item->setSelected(true); + menuList->scrollToItem(item); + } + list->setRootMenu(parent); + } + break; + case fullMode: + list = configList; + break; + default: + break; + } + + if (list) { + item = list->findConfigItem(menu); + if (item) { + item->setSelected(true); + list->scrollToItem(item); + list->setFocus(); + } + } +} + +void ConfigMainWindow::listFocusChanged(void) +{ + if (menuList->mode == menuMode) + configList->clearSelection(); +} + +void ConfigMainWindow::goBack(void) +{ + ConfigItem* item, *oldSelection; + + configList->setParentMenu(); + if (configList->rootEntry == &rootmenu) + backAction->setEnabled(false); + + if (menuList->selectedItems().count() == 0) + return; + + item = (ConfigItem*)menuList->selectedItems().first(); + oldSelection = item; + while (item) { + if (item->menu == configList->rootEntry) { + oldSelection->setSelected(false); + item->setSelected(true); + break; + } + item = (ConfigItem*)item->parent(); + } +} + +void ConfigMainWindow::showSingleView(void) +{ + singleViewAction->setEnabled(false); + singleViewAction->setChecked(true); + splitViewAction->setEnabled(true); + splitViewAction->setChecked(false); + fullViewAction->setEnabled(true); + fullViewAction->setChecked(false); + + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = singleMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setFocus(); +} + +void ConfigMainWindow::showSplitView(void) +{ + singleViewAction->setEnabled(true); + singleViewAction->setChecked(false); + splitViewAction->setEnabled(false); + splitViewAction->setChecked(true); + fullViewAction->setEnabled(true); + fullViewAction->setChecked(false); + + configList->mode = symbolMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setAllOpen(true); + configApp->processEvents(); + menuList->mode = menuMode; + menuList->setRootMenu(&rootmenu); + menuList->setAllOpen(true); + menuView->show(); + menuList->setFocus(); +} + +void ConfigMainWindow::showFullView(void) +{ + singleViewAction->setEnabled(true); + singleViewAction->setChecked(false); + splitViewAction->setEnabled(true); + splitViewAction->setChecked(false); + fullViewAction->setEnabled(false); + fullViewAction->setChecked(true); + + menuView->hide(); + menuList->setRootMenu(0); + configList->mode = fullMode; + if (configList->rootEntry == &rootmenu) + configList->updateListAll(); + else + configList->setRootMenu(&rootmenu); + configList->setFocus(); +} + +/* + * ask for saving configuration before quitting + * TODO ask only when something changed + */ +void ConfigMainWindow::closeEvent(QCloseEvent* e) +{ + if (!conf_get_changed()) { + e->accept(); + return; + } + QMessageBox mb("qconf", _("Save configuration?"), QMessageBox::Warning, + QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); + mb.setButtonText(QMessageBox::Yes, _("&Save Changes")); + mb.setButtonText(QMessageBox::No, _("&Discard Changes")); + mb.setButtonText(QMessageBox::Cancel, _("Cancel Exit")); + switch (mb.exec()) { + case QMessageBox::Yes: + if (saveConfig()) + e->accept(); + else + e->ignore(); + break; + case QMessageBox::No: + e->accept(); + break; + case QMessageBox::Cancel: + e->ignore(); + break; + } +} + +void ConfigMainWindow::showIntro(void) +{ + static const QString str = _("Welcome to the qconf graphical configuration tool.\n\n" + "For each option, a blank box indicates the feature is disabled, a check\n" + "indicates it is enabled, and a dot indicates that it is to be compiled\n" + "as a module. Clicking on the box will cycle through the three states.\n\n" + "If you do not see an option (e.g., a device driver) that you believe\n" + "should be present, try turning on Show All Options under the Options menu.\n" + "Although there is no cross reference yet to help you figure out what other\n" + "options must be enabled to support the option you are interested in, you can\n" + "still view the help of a grayed-out option.\n\n" + "Toggling Show Debug Info under the Options menu will show the dependencies,\n" + "which you can then match by examining other options.\n\n"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::showAbout(void) +{ + static const QString str = _("qconf is Copyright (C) 2002 Roman Zippel .\n" + "Copyright (C) 2015 Boris Barbulovski .\n\n" + "Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"); + + QMessageBox::information(this, "qconf", str); +} + +void ConfigMainWindow::saveSettings(void) +{ + configSettings->setValue("/window x", pos().x()); + configSettings->setValue("/window y", pos().y()); + configSettings->setValue("/window width", size().width()); + configSettings->setValue("/window height", size().height()); + + QString entry; + switch(configList->mode) { + case singleMode : + entry = "single"; + break; + + case symbolMode : + entry = "split"; + break; + + case fullMode : + entry = "full"; + break; + + default: + break; + } + configSettings->setValue("/listMode", entry); + + configSettings->writeSizes("/split1", split1->sizes()); + configSettings->writeSizes("/split2", split2->sizes()); +} + +void ConfigMainWindow::conf_changed(void) +{ + if (saveAction) + saveAction->setEnabled(conf_get_changed()); +} + +void fixup_rootmenu(struct menu *menu) +{ + struct menu *child; + static int menu_cnt = 0; + + menu->flags |= MENU_ROOT; + for (child = menu->list; child; child = child->next) { + if (child->prompt && child->prompt->type == P_MENU) { + menu_cnt++; + fixup_rootmenu(child); + menu_cnt--; + } else if (!menu_cnt) + fixup_rootmenu(child); + } +} + +static const char *progname; + +static void usage(void) +{ + printf(_("%s [-s] \n").toLatin1().constData(), progname); + exit(0); +} + +int main(int ac, char** av) +{ + ConfigMainWindow* v; + const char *name; + + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + progname = av[0]; + configApp = new QApplication(ac, av); + if (ac > 1 && av[1][0] == '-') { + switch (av[1][1]) { + case 's': + conf_set_message_callback(NULL); + break; + case 'h': + case '?': + usage(); + } + name = av[2]; + } else + name = av[1]; + if (!name) + usage(); + + conf_parse(name); + fixup_rootmenu(&rootmenu); + conf_read(NULL); + //zconfdump(stdout); + + configSettings = new ConfigSettings(); + configSettings->beginGroup("/kconfig/qconf"); + v = new ConfigMainWindow(); + + //zconfdump(stdout); + configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); + configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + v->show(); + configApp->exec(); + + configSettings->endGroup(); + delete configSettings; + delete v; + delete configApp; + + return 0; +} diff --git a/tools/kconfig/qconf.h b/tools/kconfig/qconf.h new file mode 100644 index 0000000..a40036d --- /dev/null +++ b/tools/kconfig/qconf.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "expr.h" + +class ConfigView; +class ConfigList; +class ConfigItem; +class ConfigLineEdit; +class ConfigMainWindow; + +class ConfigSettings : public QSettings { +public: + ConfigSettings(); + QList readSizes(const QString& key, bool *ok); + bool writeSizes(const QString& key, const QList& value); +}; + +enum colIdx { + promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr +}; +enum listMode { + singleMode, menuMode, symbolMode, fullMode, listMode +}; +enum optionMode { + normalOpt = 0, allOpt, promptOpt +}; + +class ConfigList : public QTreeWidget { + Q_OBJECT + typedef class QTreeWidget Parent; +public: + ConfigList(ConfigView* p, const char *name = 0); + void reinit(void); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + ConfigItem* findConfigItem(struct menu *); + +protected: + void keyPressEvent(QKeyEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void focusInEvent(QFocusEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + +public slots: + void setRootMenu(struct menu *menu); + + void updateList(ConfigItem *item); + void setValue(ConfigItem* item, tristate val); + void changeValue(ConfigItem* item); + void updateSelection(void); + void saveSettings(void); +signals: + void menuChanged(struct menu *menu); + void menuSelected(struct menu *menu); + void parentSelected(void); + void gotFocus(struct menu *); + +public: + void updateListAll(void) + { + updateAll = true; + updateList(NULL); + updateAll = false; + } + ConfigList* listView() + { + return this; + } + ConfigItem* firstChild() const + { + return (ConfigItem *)children().first(); + } + void addColumn(colIdx idx) + { + showColumn(idx); + } + void removeColumn(colIdx idx) + { + hideColumn(idx); + } + void setAllOpen(bool open); + void setParentMenu(void); + + bool menuSkip(struct menu *); + + void updateMenuList(ConfigItem *parent, struct menu*); + void updateMenuList(ConfigList *parent, struct menu*); + + bool updateAll; + + QPixmap symbolYesPix, symbolModPix, symbolNoPix; + QPixmap choiceYesPix, choiceNoPix; + QPixmap menuPix, menuInvPix, menuBackPix, voidPix; + + bool showName, showRange, showData; + enum listMode mode; + enum optionMode optMode; + struct menu *rootEntry; + QPalette disabledColorGroup; + QPalette inactivedColorGroup; + QMenu* headerPopup; +}; + +class ConfigItem : public QTreeWidgetItem { + typedef class QTreeWidgetItem Parent; +public: + ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v) + : Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false) + { + init(); + } + ConfigItem(ConfigList *parent, ConfigItem *after, bool v) + : Parent(parent, after), nextItem(0), menu(0), visible(v), goParent(true) + { + init(); + } + ~ConfigItem(void); + void init(void); + void okRename(int col); + void updateMenu(void); + void testUpdateMenu(bool v); + ConfigList* listView() const + { + return (ConfigList*)Parent::treeWidget(); + } + ConfigItem* firstChild() const + { + return (ConfigItem *)Parent::child(0); + } + ConfigItem* nextSibling() + { + ConfigItem *ret = NULL; + ConfigItem *_parent = (ConfigItem *)parent(); + + if(_parent) { + ret = (ConfigItem *)_parent->child(_parent->indexOfChild(this)+1); + } else { + QTreeWidget *_treeWidget = treeWidget(); + ret = (ConfigItem *)_treeWidget->topLevelItem(_treeWidget->indexOfTopLevelItem(this)+1); + } + + return ret; + } + void setText(colIdx idx, const QString& text) + { + Parent::setText(idx, text); + } + QString text(colIdx idx) const + { + return Parent::text(idx); + } + void setPixmap(colIdx idx, const QIcon &icon) + { + Parent::setIcon(idx, icon); + } + const QIcon pixmap(colIdx idx) const + { + return icon(idx); + } + // TODO: Implement paintCell + + ConfigItem* nextItem; + struct menu *menu; + bool visible; + bool goParent; +}; + +class ConfigLineEdit : public QLineEdit { + Q_OBJECT + typedef class QLineEdit Parent; +public: + ConfigLineEdit(ConfigView* parent); + ConfigView* parent(void) const + { + return (ConfigView*)Parent::parent(); + } + void show(ConfigItem *i); + void keyPressEvent(QKeyEvent *e); + +public: + ConfigItem *item; +}; + +class ConfigView : public QWidget { + Q_OBJECT + typedef class QWidget Parent; +public: + ConfigView(QWidget* parent, const char *name = 0); + ~ConfigView(void); + static void updateList(ConfigItem* item); + static void updateListAll(void); + + bool showName(void) const { return list->showName; } + bool showRange(void) const { return list->showRange; } + bool showData(void) const { return list->showData; } +public slots: + void setShowName(bool); + void setShowRange(bool); + void setShowData(bool); + void setOptionMode(QAction *); +signals: + void showNameChanged(bool); + void showRangeChanged(bool); + void showDataChanged(bool); +public: + ConfigList* list; + ConfigLineEdit* lineEdit; + + static ConfigView* viewList; + ConfigView* nextView; + + static QAction *showNormalAction; + static QAction *showAllAction; + static QAction *showPromptAction; +}; + +class ConfigInfoView : public QTextBrowser { + Q_OBJECT + typedef class QTextBrowser Parent; +public: + ConfigInfoView(QWidget* parent, const char *name = 0); + bool showDebug(void) const { return _showDebug; } + +public slots: + void setInfo(struct menu *menu); + void saveSettings(void); + void setShowDebug(bool); + +signals: + void showDebugChanged(bool); + void menuSelected(struct menu *); + +protected: + void symbolInfo(void); + void menuInfo(void); + QString debug_info(struct symbol *sym); + static QString print_filter(const QString &str); + static void expr_print_help(void *data, struct symbol *sym, const char *str); + QMenu *createStandardContextMenu(const QPoint & pos); + void contextMenuEvent(QContextMenuEvent *e); + + struct symbol *sym; + struct menu *_menu; + bool _showDebug; +}; + +class ConfigSearchWindow : public QDialog { + Q_OBJECT + typedef class QDialog Parent; +public: + ConfigSearchWindow(ConfigMainWindow* parent, const char *name = 0); + +public slots: + void saveSettings(void); + void search(void); + +protected: + QLineEdit* editField; + QPushButton* searchButton; + QSplitter* split; + ConfigView* list; + ConfigInfoView* info; + + struct symbol **result; +}; + +class ConfigMainWindow : public QMainWindow { + Q_OBJECT + + static QAction *saveAction; + static void conf_changed(void); +public: + ConfigMainWindow(void); +public slots: + void changeMenu(struct menu *); + void setMenuLink(struct menu *); + void listFocusChanged(void); + void goBack(void); + void loadConfig(void); + bool saveConfig(void); + void saveConfigAs(void); + void searchConfig(void); + void showSingleView(void); + void showSplitView(void); + void showFullView(void); + void showIntro(void); + void showAbout(void); + void saveSettings(void); + +protected: + void closeEvent(QCloseEvent *e); + + ConfigSearchWindow *searchWindow; + ConfigView *menuView; + ConfigList *menuList; + ConfigView *configView; + ConfigList *configList; + ConfigInfoView *helpText; + QToolBar *toolBar; + QAction *backAction; + QAction *singleViewAction; + QAction *splitViewAction; + QAction *fullViewAction; + QSplitter *split1; + QSplitter *split2; +}; diff --git a/tools/kconfig/streamline_config.pl b/tools/kconfig/streamline_config.pl new file mode 100755 index 0000000..a2e83ab --- /dev/null +++ b/tools/kconfig/streamline_config.pl @@ -0,0 +1,682 @@ +#!/usr/bin/env perl +# +# Copyright 2005-2009 - Steven Rostedt +# Licensed under the terms of the GNU GPL License version 2 +# +# It's simple enough to figure out how this works. +# If not, then you can ask me at stripconfig@goodmis.org +# +# What it does? +# +# If you have installed a Linux kernel from a distribution +# that turns on way too many modules than you need, and +# you only want the modules you use, then this program +# is perfect for you. +# +# It gives you the ability to turn off all the modules that are +# not loaded on your system. +# +# Howto: +# +# 1. Boot up the kernel that you want to stream line the config on. +# 2. Change directory to the directory holding the source of the +# kernel that you just booted. +# 3. Copy the configuraton file to this directory as .config +# 4. Have all your devices that you need modules for connected and +# operational (make sure that their corresponding modules are loaded) +# 5. Run this script redirecting the output to some other file +# like config_strip. +# 6. Back up your old config (if you want too). +# 7. copy the config_strip file to .config +# 8. Run "make oldconfig" +# +# Now your kernel is ready to be built with only the modules that +# are loaded. +# +# Here's what I did with my Debian distribution. +# +# cd /usr/src/linux-2.6.10 +# cp /boot/config-2.6.10-1-686-smp .config +# ~/bin/streamline_config > config_strip +# mv .config config_sav +# mv config_strip .config +# make oldconfig +# +use warnings; +use strict; +use Getopt::Long; + +# set the environment variable LOCALMODCONFIG_DEBUG to get +# debug output. +my $debugprint = 0; +$debugprint = 1 if (defined($ENV{LOCALMODCONFIG_DEBUG})); + +sub dprint { + return if (!$debugprint); + print STDERR @_; +} + +my $config = ".config"; + +my $uname = `uname -r`; +chomp $uname; + +my @searchconfigs = ( + { + "file" => ".config", + "exec" => "cat", + }, + { + "file" => "/proc/config.gz", + "exec" => "zcat", + }, + { + "file" => "/boot/config-$uname", + "exec" => "cat", + }, + { + "file" => "/boot/vmlinuz-$uname", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "vmlinux", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "/lib/modules/$uname/kernel/kernel/configs.ko", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "kernel/configs.ko", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, + { + "file" => "kernel/configs.o", + "exec" => "scripts/extract-ikconfig", + "test" => "scripts/extract-ikconfig", + }, +); + +sub read_config { + foreach my $conf (@searchconfigs) { + my $file = $conf->{"file"}; + + next if ( ! -f "$file"); + + if (defined($conf->{"test"})) { + `$conf->{"test"} $conf->{"file"} 2>/dev/null`; + next if ($?); + } + + my $exec = $conf->{"exec"}; + + print STDERR "using config: '$file'\n"; + + open(my $infile, '-|', "$exec $file") || die "Failed to run $exec $file"; + my @x = <$infile>; + close $infile; + return @x; + } + die "No config file found"; +} + +my @config_file = read_config; + +# Parse options +my $localmodconfig = 0; +my $localyesconfig = 0; + +GetOptions("localmodconfig" => \$localmodconfig, + "localyesconfig" => \$localyesconfig); + +# Get the build source and top level Kconfig file (passed in) +my $ksource = ($ARGV[0] ? $ARGV[0] : '.'); +my $kconfig = $ARGV[1]; +my $lsmod_file = $ENV{'LSMOD'}; + +my @makefiles = `find $ksource -name Makefile -or -name Kbuild 2>/dev/null`; +chomp @makefiles; + +my %depends; +my %selects; +my %prompts; +my %objects; +my $var; +my $iflevel = 0; +my @ifdeps; + +# prevent recursion +my %read_kconfigs; + +sub read_kconfig { + my ($kconfig) = @_; + + my $state = "NONE"; + my $config; + + my $cont = 0; + my $line; + + my $source = "$ksource/$kconfig"; + my $last_source = ""; + + # Check for any environment variables used + while ($source =~ /\$(\w+)/ && $last_source ne $source) { + my $env = $1; + $last_source = $source; + $source =~ s/\$$env/$ENV{$env}/; + } + + open(my $kinfile, '<', $source) || die "Can't open $kconfig"; + while (<$kinfile>) { + chomp; + + # Make sure that lines ending with \ continue + if ($cont) { + $_ = $line . " " . $_; + } + + if (s/\\$//) { + $cont = 1; + $line = $_; + next; + } + + $cont = 0; + + # collect any Kconfig sources + if (/^source\s+"?([^"]+)/) { + my $kconfig = $1; + # prevent reading twice. + if (!defined($read_kconfigs{$kconfig})) { + $read_kconfigs{$kconfig} = 1; + read_kconfig($kconfig); + } + next; + } + + # configs found + if (/^\s*(menu)?config\s+(\S+)\s*$/) { + $state = "NEW"; + $config = $2; + + # Add depends for 'if' nesting + for (my $i = 0; $i < $iflevel; $i++) { + if ($i) { + $depends{$config} .= " " . $ifdeps[$i]; + } else { + $depends{$config} = $ifdeps[$i]; + } + $state = "DEP"; + } + + # collect the depends for the config + } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) { + $state = "DEP"; + $depends{$config} = $1; + } elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) { + $depends{$config} .= " " . $1; + } elsif ($state eq "DEP" && /^\s*def(_(bool|tristate)|ault)\s+(\S.*)$/) { + my $dep = $3; + if ($dep !~ /^\s*(y|m|n)\s*$/) { + $dep =~ s/.*\sif\s+//; + $depends{$config} .= " " . $dep; + dprint "Added default depends $dep to $config\n"; + } + + # Get the configs that select this config + } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) { + my $conf = $1; + if (defined($selects{$conf})) { + $selects{$conf} .= " " . $config; + } else { + $selects{$conf} = $config; + } + + # configs without prompts must be selected + } elsif ($state ne "NONE" && /^\s*(tristate\s+\S|prompt\b)/) { + # note if the config has a prompt + $prompts{$config} = 1; + + # Check for if statements + } elsif (/^if\s+(.*\S)\s*$/) { + my $deps = $1; + # remove beginning and ending non text + $deps =~ s/^[^a-zA-Z0-9_]*//; + $deps =~ s/[^a-zA-Z0-9_]*$//; + + my @deps = split /[^a-zA-Z0-9_]+/, $deps; + + $ifdeps[$iflevel++] = join ':', @deps; + + } elsif (/^endif/) { + + $iflevel-- if ($iflevel); + + # stop on "help" and keywords that end a menu entry + } elsif (/^\s*(---)?help(---)?\s*$/ || /^(comment|choice|menu)\b/) { + $state = "NONE"; + } + } + close($kinfile); +} + +if ($kconfig) { + read_kconfig($kconfig); +} + +# Makefiles can use variables to define their dependencies +sub convert_vars { + my ($line, %vars) = @_; + + my $process = ""; + + while ($line =~ s/^(.*?)(\$\((.*?)\))//) { + my $start = $1; + my $variable = $2; + my $var = $3; + + if (defined($vars{$var})) { + $process .= $start . $vars{$var}; + } else { + $process .= $start . $variable; + } + } + + $process .= $line; + + return $process; +} + +# Read all Makefiles to map the configs to the objects +foreach my $makefile (@makefiles) { + + my $line = ""; + my %make_vars; + + open(my $infile, '<', $makefile) || die "Can't open $makefile"; + while (<$infile>) { + # if this line ends with a backslash, continue + chomp; + if (/^(.*)\\$/) { + $line .= $1; + next; + } + + $line .= $_; + $_ = $line; + $line = ""; + + my $objs; + + # Convert variables in a line (could define configs) + $_ = convert_vars($_, %make_vars); + + # collect objects after obj-$(CONFIG_FOO_BAR) + if (/obj-\$\((CONFIG_[^\)]*)\)\s*[+:]?=\s*(.*)/) { + $var = $1; + $objs = $2; + + # check if variables are set + } elsif (/^\s*(\S+)\s*[:]?=\s*(.*\S)/) { + $make_vars{$1} = $2; + } + if (defined($objs)) { + foreach my $obj (split /\s+/,$objs) { + $obj =~ s/-/_/g; + if ($obj =~ /(.*)\.o$/) { + # Objects may be enabled by more than one config. + # Store configs in an array. + my @arr; + + if (defined($objects{$1})) { + @arr = @{$objects{$1}}; + } + + $arr[$#arr+1] = $var; + + # The objects have a hash mapping to a reference + # of an array of configs. + $objects{$1} = \@arr; + } + } + } + } + close($infile); +} + +my %modules; +my $linfile; + +if (defined($lsmod_file)) { + if ( ! -f $lsmod_file) { + if ( -f $ENV{'objtree'}."/".$lsmod_file) { + $lsmod_file = $ENV{'objtree'}."/".$lsmod_file; + } else { + die "$lsmod_file not found"; + } + } + + my $otype = ( -x $lsmod_file) ? '-|' : '<'; + open($linfile, $otype, $lsmod_file); + +} else { + + # see what modules are loaded on this system + my $lsmod; + + foreach my $dir ( ("/sbin", "/bin", "/usr/sbin", "/usr/bin") ) { + if ( -x "$dir/lsmod" ) { + $lsmod = "$dir/lsmod"; + last; + } +} + if (!defined($lsmod)) { + # try just the path + $lsmod = "lsmod"; + } + + open($linfile, '-|', $lsmod) || die "Can not call lsmod with $lsmod"; +} + +while (<$linfile>) { + next if (/^Module/); # Skip the first line. + if (/^(\S+)/) { + $modules{$1} = 1; + } +} +close ($linfile); + +# add to the configs hash all configs that are needed to enable +# a loaded module. This is a direct obj-${CONFIG_FOO} += bar.o +# where we know we need bar.o so we add FOO to the list. +my %configs; +foreach my $module (keys(%modules)) { + if (defined($objects{$module})) { + my @arr = @{$objects{$module}}; + foreach my $conf (@arr) { + $configs{$conf} = $module; + dprint "$conf added by direct ($module)\n"; + if ($debugprint) { + my $c=$conf; + $c =~ s/^CONFIG_//; + if (defined($depends{$c})) { + dprint " deps = $depends{$c}\n"; + } else { + dprint " no deps\n"; + } + } + } + } else { + # Most likely, someone has a custom (binary?) module loaded. + print STDERR "$module config not found!!\n"; + } +} + +# Read the current config, and see what is enabled. We want to +# ignore configs that we would not enable anyway. + +my %orig_configs; +my $valid = "A-Za-z_0-9"; + +foreach my $line (@config_file) { + $_ = $line; + + if (/(CONFIG_[$valid]*)=(m|y)/) { + $orig_configs{$1} = $2; + } +} + +my $repeat = 1; + +my $depconfig; + +# +# Note, we do not care about operands (like: &&, ||, !) we want to add any +# config that is in the depend list of another config. This script does +# not enable configs that are not already enabled. If we come across a +# config A that depends on !B, we can still add B to the list of depends +# to keep on. If A was on in the original config, B would not have been +# and B would not be turned on by this script. +# +sub parse_config_depends +{ + my ($p) = @_; + + while ($p =~ /[$valid]/) { + + if ($p =~ /^[^$valid]*([$valid]+)/) { + my $conf = "CONFIG_" . $1; + + $p =~ s/^[^$valid]*[$valid]+//; + + # We only need to process if the depend config is a module + if (!defined($orig_configs{$conf}) || $orig_configs{$conf} eq "y") { + next; + } + + if (!defined($configs{$conf})) { + # We must make sure that this config has its + # dependencies met. + $repeat = 1; # do again + dprint "$conf selected by depend $depconfig\n"; + $configs{$conf} = 1; + } + } else { + die "this should never happen"; + } + } +} + +# Select is treated a bit differently than depends. We call this +# when a config has no prompt and requires another config to be +# selected. We use to just select all configs that selected this +# config, but found that that can balloon into enabling hundreds +# of configs that we do not care about. +# +# The idea is we look at all the configs that select it. If one +# is already in our list of configs to enable, then there's nothing +# else to do. If there isn't, we pick the first config that was +# enabled in the orignal config and use that. +sub parse_config_selects +{ + my ($config, $p) = @_; + + my $next_config; + + while ($p =~ /[$valid]/) { + + if ($p =~ /^[^$valid]*([$valid]+)/) { + my $conf = "CONFIG_" . $1; + + $p =~ s/^[^$valid]*[$valid]+//; + + # Make sure that this config exists in the current .config file + if (!defined($orig_configs{$conf})) { + dprint "$conf not set for $config select\n"; + next; + } + + # Check if something other than a module selects this config + if (defined($orig_configs{$conf}) && $orig_configs{$conf} ne "m") { + dprint "$conf (non module) selects config, we are good\n"; + # we are good with this + return; + } + if (defined($configs{$conf})) { + dprint "$conf selects $config so we are good\n"; + # A set config selects this config, we are good + return; + } + # Set this config to be selected + if (!defined($next_config)) { + $next_config = $conf; + } + } else { + die "this should never happen"; + } + } + + # If no possible config selected this, then something happened. + if (!defined($next_config)) { + print STDERR "WARNING: $config is required, but nothing in the\n"; + print STDERR " current config selects it.\n"; + return; + } + + # If we are here, then we found no config that is set and + # selects this config. Repeat. + $repeat = 1; + # Make this config need to be selected + $configs{$next_config} = 1; + dprint "$next_config selected by select $config\n"; +} + +my %process_selects; + +# loop through all configs, select their dependencies. +sub loop_depend { + $repeat = 1; + + while ($repeat) { + $repeat = 0; + + forloop: + foreach my $config (keys %configs) { + + # If this config is not a module, we do not need to process it + if (defined($orig_configs{$config}) && $orig_configs{$config} ne "m") { + next forloop; + } + + $config =~ s/^CONFIG_//; + $depconfig = $config; + + if (defined($depends{$config})) { + # This config has dependencies. Make sure they are also included + parse_config_depends $depends{$config}; + } + + # If the config has no prompt, then we need to check if a config + # that is enabled selected it. Or if we need to enable one. + if (!defined($prompts{$config}) && defined($selects{$config})) { + $process_selects{$config} = 1; + } + } + } +} + +sub loop_select { + + foreach my $config (keys %process_selects) { + $config =~ s/^CONFIG_//; + + dprint "Process select $config\n"; + + # config has no prompt and must be selected. + parse_config_selects $config, $selects{$config}; + } +} + +while ($repeat) { + # Get the first set of configs and their dependencies. + loop_depend; + + $repeat = 0; + + # Now we need to see if we have to check selects; + loop_select; +} + +my %setconfigs; + +# Finally, read the .config file and turn off any module enabled that +# we could not find a reason to keep enabled. +foreach my $line (@config_file) { + $_ = $line; + + if (/CONFIG_IKCONFIG/) { + if (/# CONFIG_IKCONFIG is not set/) { + # enable IKCONFIG at least as a module + print "CONFIG_IKCONFIG=m\n"; + # don't ask about PROC + print "# CONFIG_IKCONFIG_PROC is not set\n"; + } else { + print; + } + next; + } + + if (/CONFIG_MODULE_SIG_KEY="(.+)"/) { + my $orig_cert = $1; + my $default_cert = "certs/signing_key.pem"; + + # Check that the logic in this script still matches the one in Kconfig + if (!defined($depends{"MODULE_SIG_KEY"}) || + $depends{"MODULE_SIG_KEY"} !~ /"\Q$default_cert\E"/) { + print STDERR "WARNING: MODULE_SIG_KEY assertion failure, ", + "update needed to ", __FILE__, " line ", __LINE__, "\n"; + print; + } elsif ($orig_cert ne $default_cert && ! -f $orig_cert) { + print STDERR "Module signature verification enabled but ", + "module signing key \"$orig_cert\" not found. Resetting ", + "signing key to default value.\n"; + print "CONFIG_MODULE_SIG_KEY=\"$default_cert\"\n"; + } else { + print; + } + next; + } + + if (/CONFIG_SYSTEM_TRUSTED_KEYS="(.+)"/) { + my $orig_keys = $1; + + if (! -f $orig_keys) { + print STDERR "System keyring enabled but keys \"$orig_keys\" ", + "not found. Resetting keys to default value.\n"; + print "CONFIG_SYSTEM_TRUSTED_KEYS=\"\"\n"; + } else { + print; + } + next; + } + + if (/^(CONFIG.*)=(m|y)/) { + if (defined($configs{$1})) { + if ($localyesconfig) { + $setconfigs{$1} = 'y'; + print "$1=y\n"; + next; + } else { + $setconfigs{$1} = $2; + } + } elsif ($2 eq "m") { + print "# $1 is not set\n"; + next; + } + } + print; +} + +# Integrity check, make sure all modules that we want enabled do +# indeed have their configs set. +loop: +foreach my $module (keys(%modules)) { + if (defined($objects{$module})) { + my @arr = @{$objects{$module}}; + foreach my $conf (@arr) { + if (defined($setconfigs{$conf})) { + next loop; + } + } + print STDERR "module $module did not have configs"; + foreach my $conf (@arr) { + print STDERR " " , $conf; + } + print STDERR "\n"; + } +} diff --git a/tools/kconfig/symbol.c b/tools/kconfig/symbol.c new file mode 100644 index 0000000..20136ff --- /dev/null +++ b/tools/kconfig/symbol.c @@ -0,0 +1,1414 @@ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#include "lkc.h" + +struct symbol symbol_yes = { + .name = "y", + .curr = { "y", yes }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_mod = { + .name = "m", + .curr = { "m", mod }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_no = { + .name = "n", + .curr = { "n", no }, + .flags = SYMBOL_CONST|SYMBOL_VALID, +}, symbol_empty = { + .name = "", + .curr = { "", no }, + .flags = SYMBOL_VALID, +}; + +struct symbol *sym_defconfig_list; +struct symbol *modules_sym; +tristate modules_val; + +struct expr *sym_env_list; + +static void sym_add_default(struct symbol *sym, const char *def) +{ + struct property *prop = prop_alloc(P_DEFAULT, sym); + + prop->expr = expr_alloc_symbol(sym_lookup(def, SYMBOL_CONST)); +} + +void sym_init(void) +{ + struct symbol *sym; + struct utsname uts; + static bool inited = false; + + if (inited) + return; + inited = true; + + uname(&uts); + + sym = sym_lookup("UNAME_RELEASE", 0); + sym->type = S_STRING; + sym->flags |= SYMBOL_AUTO; + sym_add_default(sym, uts.release); +} + +enum symbol_type sym_get_type(struct symbol *sym) +{ + enum symbol_type type = sym->type; + + if (type == S_TRISTATE) { + if (sym_is_choice_value(sym) && sym->visible == yes) + type = S_BOOLEAN; + else if (modules_val == no) + type = S_BOOLEAN; + } + return type; +} + +const char *sym_type_name(enum symbol_type type) +{ + switch (type) { + case S_BOOLEAN: + return "boolean"; + case S_TRISTATE: + return "tristate"; + case S_INT: + return "integer"; + case S_HEX: + return "hex"; + case S_STRING: + return "string"; + case S_UNKNOWN: + return "unknown"; + case S_OTHER: + break; + } + return "???"; +} + +struct property *sym_get_choice_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_choices(sym, prop) + return prop; + return NULL; +} + +struct property *sym_get_env_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_ENV) + return prop; + return NULL; +} + +static struct property *sym_get_default_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static struct property *sym_get_range_prop(struct symbol *sym) +{ + struct property *prop; + + for_all_properties(sym, prop, P_RANGE) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri != no) + return prop; + } + return NULL; +} + +static long long sym_get_range_val(struct symbol *sym, int base) +{ + sym_calc_value(sym); + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + break; + } + return strtoll(sym->curr.val, NULL, base); +} + +static void sym_validate_range(struct symbol *sym) +{ + struct property *prop; + int base; + long long val, val2; + char str[64]; + + switch (sym->type) { + case S_INT: + base = 10; + break; + case S_HEX: + base = 16; + break; + default: + return; + } + prop = sym_get_range_prop(sym); + if (!prop) + return; + val = strtoll(sym->curr.val, NULL, base); + val2 = sym_get_range_val(prop->expr->left.sym, base); + if (val >= val2) { + val2 = sym_get_range_val(prop->expr->right.sym, base); + if (val <= val2) + return; + } + if (sym->type == S_INT) + sprintf(str, "%lld", val2); + else + sprintf(str, "0x%llx", val2); + sym->curr.val = strdup(str); +} + +static void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +static void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + +static void sym_calc_visibility(struct symbol *sym) +{ + struct property *prop; + struct symbol *choice_sym = NULL; + tristate tri; + + /* any prompt visible? */ + tri = no; + + if (sym_is_choice_value(sym)) + choice_sym = prop_get_symbol(sym_get_choice_prop(sym)); + + for_all_prompts(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + /* + * Tristate choice_values with visibility 'mod' are + * not visible if the corresponding choice's value is + * 'yes'. + */ + if (choice_sym && sym->type == S_TRISTATE && + prop->visible.tri == mod && choice_sym->curr.tri == yes) + prop->visible.tri = no; + + tri = EXPR_OR(tri, prop->visible.tri); + } + if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) + tri = yes; + if (sym->visible != tri) { + sym->visible = tri; + sym_set_changed(sym); + } + if (sym_is_choice_value(sym)) + return; + /* defaulting to "yes" if no explicit "depends on" are given */ + tri = yes; + if (sym->dir_dep.expr) + tri = expr_calc_value(sym->dir_dep.expr); + if (tri == mod) + tri = yes; + if (sym->dir_dep.tri != tri) { + sym->dir_dep.tri = tri; + sym_set_changed(sym); + } + tri = no; + if (sym->rev_dep.expr) + tri = expr_calc_value(sym->rev_dep.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->rev_dep.tri != tri) { + sym->rev_dep.tri = tri; + sym_set_changed(sym); + } + tri = no; + if (sym->implied.expr && sym->dir_dep.tri != no) + tri = expr_calc_value(sym->implied.expr); + if (tri == mod && sym_get_type(sym) == S_BOOLEAN) + tri = yes; + if (sym->implied.tri != tri) { + sym->implied.tri = tri; + sym_set_changed(sym); + } +} + +/* + * Find the default symbol for a choice. + * First try the default values for the choice symbol + * Next locate the first visible choice value + * Return NULL if none was found + */ +struct symbol *sym_choice_default(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + + /* any of the defaults visible? */ + for_all_defaults(sym, prop) { + prop->visible.tri = expr_calc_value(prop->visible.expr); + if (prop->visible.tri == no) + continue; + def_sym = prop_get_symbol(prop); + if (def_sym->visible != no) + return def_sym; + } + + /* just get the first visible value */ + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) + if (def_sym->visible != no) + return def_sym; + + /* failed to locate any defaults */ + return NULL; +} + +static struct symbol *sym_calc_choice(struct symbol *sym) +{ + struct symbol *def_sym; + struct property *prop; + struct expr *e; + int flags; + + /* first calculate all choice values' visibilities */ + flags = sym->flags; + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, def_sym) { + sym_calc_visibility(def_sym); + if (def_sym->visible != no) + flags &= def_sym->flags; + } + + sym->flags &= flags | ~SYMBOL_DEF_USER; + + /* is the user choice visible? */ + def_sym = sym->def[S_DEF_USER].val; + if (def_sym && def_sym->visible != no) + return def_sym; + + def_sym = sym_choice_default(sym); + + if (def_sym == NULL) + /* no choice? reset tristate value */ + sym->curr.tri = no; + + return def_sym; +} + +void sym_calc_value(struct symbol *sym) +{ + struct symbol_value newval, oldval; + struct property *prop; + struct expr *e; + + if (!sym) + return; + + if (sym->flags & SYMBOL_VALID) + return; + + if (sym_is_choice_value(sym) && + sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) { + sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES; + prop = sym_get_choice_prop(sym); + sym_calc_value(prop_get_symbol(prop)); + } + + sym->flags |= SYMBOL_VALID; + + oldval = sym->curr; + + switch (sym->type) { + case S_INT: + case S_HEX: + case S_STRING: + newval = symbol_empty.curr; + break; + case S_BOOLEAN: + case S_TRISTATE: + newval = symbol_no.curr; + break; + default: + sym->curr.val = sym->name; + sym->curr.tri = no; + return; + } + if (!sym_is_choice_value(sym)) + sym->flags &= ~SYMBOL_WRITE; + + sym_calc_visibility(sym); + + /* set default if recursively called */ + sym->curr = newval; + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_is_choice_value(sym) && sym->visible == yes) { + prop = sym_get_choice_prop(sym); + newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no; + } else { + if (sym->visible != no) { + /* if the symbol is visible use the user value + * if available, otherwise try the default value + */ + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, + sym->visible); + goto calc_newval; + } + } + if (sym->rev_dep.tri != no) + sym->flags |= SYMBOL_WRITE; + if (!sym_is_choice(sym)) { + prop = sym_get_default_prop(sym); + if (prop) { + sym->flags |= SYMBOL_WRITE; + newval.tri = EXPR_AND(expr_calc_value(prop->expr), + prop->visible.tri); + } + if (sym->implied.tri != no) { + sym->flags |= SYMBOL_WRITE; + newval.tri = EXPR_OR(newval.tri, sym->implied.tri); + } + } + calc_newval: + if (sym->dir_dep.tri == no && sym->rev_dep.tri != no) { + struct expr *e; + e = expr_simplify_unmet_dep(sym->rev_dep.expr, + sym->dir_dep.expr); + fprintf(stderr, "warning: ("); + expr_fprint(e, stderr); + fprintf(stderr, ") selects %s which has unmet direct dependencies (", + sym->name); + expr_fprint(sym->dir_dep.expr, stderr); + fprintf(stderr, ")\n"); + expr_free(e); + } + newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); + } + if (newval.tri == mod && + (sym_get_type(sym) == S_BOOLEAN || sym->implied.tri == yes)) + newval.tri = yes; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (sym->visible != no) { + sym->flags |= SYMBOL_WRITE; + if (sym_has_value(sym)) { + newval.val = sym->def[S_DEF_USER].val; + break; + } + } + prop = sym_get_default_prop(sym); + if (prop) { + struct symbol *ds = prop_get_symbol(prop); + if (ds) { + sym->flags |= SYMBOL_WRITE; + sym_calc_value(ds); + newval.val = ds->curr.val; + } + } + break; + default: + ; + } + + sym->curr = newval; + if (sym_is_choice(sym) && newval.tri == yes) + sym->curr.val = sym_calc_choice(sym); + sym_validate_range(sym); + + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { + sym_set_changed(sym); + if (modules_sym == sym) { + sym_set_all_changed(); + modules_val = modules_sym->curr.tri; + } + } + + if (sym_is_choice(sym)) { + struct symbol *choice_sym; + + prop = sym_get_choice_prop(sym); + expr_list_for_each_sym(prop->expr, e, choice_sym) { + if ((sym->flags & SYMBOL_WRITE) && + choice_sym->visible != no) + choice_sym->flags |= SYMBOL_WRITE; + if (sym->flags & SYMBOL_CHANGED) + sym_set_changed(choice_sym); + } + } + + if (sym->flags & SYMBOL_AUTO) + sym->flags &= ~SYMBOL_WRITE; + + if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) + set_all_choice_values(sym); +} + +void sym_clear_all_valid(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym->flags &= ~SYMBOL_VALID; + sym_add_change_count(1); + sym_calc_value(modules_sym); +} + +bool sym_tristate_within_range(struct symbol *sym, tristate val) +{ + int type = sym_get_type(sym); + + if (sym->visible == no) + return false; + + if (type != S_BOOLEAN && type != S_TRISTATE) + return false; + + if (type == S_BOOLEAN && val == mod) + return false; + if (sym->visible <= sym->rev_dep.tri) + return false; + if (sym->implied.tri == yes && val == mod) + return false; + if (sym_is_choice_value(sym) && sym->visible == yes) + return val == yes; + return val >= sym->rev_dep.tri && val <= sym->visible; +} + +bool sym_set_tristate_value(struct symbol *sym, tristate val) +{ + tristate oldval = sym_get_tristate_value(sym); + + if (oldval != val && !sym_tristate_within_range(sym, val)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + /* + * setting a choice value also resets the new flag of the choice + * symbol and all other choice values. + */ + if (sym_is_choice_value(sym) && val == yes) { + struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); + struct property *prop; + struct expr *e; + + cs->def[S_DEF_USER].val = sym; + cs->flags |= SYMBOL_DEF_USER; + prop = sym_get_choice_prop(cs); + for (e = prop->expr; e; e = e->left.expr) { + if (e->right.sym->visible != no) + e->right.sym->flags |= SYMBOL_DEF_USER; + } + } + + sym->def[S_DEF_USER].tri = val; + if (oldval != val) + sym_clear_all_valid(); + + return true; +} + +tristate sym_toggle_tristate_value(struct symbol *sym) +{ + tristate oldval, newval; + + oldval = newval = sym_get_tristate_value(sym); + do { + switch (newval) { + case no: + newval = mod; + break; + case mod: + newval = yes; + break; + case yes: + newval = no; + break; + } + if (sym_set_tristate_value(sym, newval)) + break; + } while (oldval != newval); + return newval; +} + +bool sym_string_valid(struct symbol *sym, const char *str) +{ + signed char ch; + + switch (sym->type) { + case S_STRING: + return true; + case S_INT: + ch = *str++; + if (ch == '-') + ch = *str++; + if (!isdigit(ch)) + return false; + if (ch == '0' && *str != 0) + return false; + while ((ch = *str++)) { + if (!isdigit(ch)) + return false; + } + return true; + case S_HEX: + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) + str += 2; + ch = *str++; + do { + if (!isxdigit(ch)) + return false; + } while ((ch = *str++)); + return true; + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + case 'm': case 'M': + case 'n': case 'N': + return true; + } + return false; + default: + return false; + } +} + +bool sym_string_within_range(struct symbol *sym, const char *str) +{ + struct property *prop; + long long val; + + switch (sym->type) { + case S_STRING: + return sym_string_valid(sym, str); + case S_INT: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtoll(str, NULL, 10); + return val >= sym_get_range_val(prop->expr->left.sym, 10) && + val <= sym_get_range_val(prop->expr->right.sym, 10); + case S_HEX: + if (!sym_string_valid(sym, str)) + return false; + prop = sym_get_range_prop(sym); + if (!prop) + return true; + val = strtoll(str, NULL, 16); + return val >= sym_get_range_val(prop->expr->left.sym, 16) && + val <= sym_get_range_val(prop->expr->right.sym, 16); + case S_BOOLEAN: + case S_TRISTATE: + switch (str[0]) { + case 'y': case 'Y': + return sym_tristate_within_range(sym, yes); + case 'm': case 'M': + return sym_tristate_within_range(sym, mod); + case 'n': case 'N': + return sym_tristate_within_range(sym, no); + } + return false; + default: + return false; + } +} + +bool sym_set_string_value(struct symbol *sym, const char *newval) +{ + const char *oldval; + char *val; + int size; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (newval[0]) { + case 'y': case 'Y': + return sym_set_tristate_value(sym, yes); + case 'm': case 'M': + return sym_set_tristate_value(sym, mod); + case 'n': case 'N': + return sym_set_tristate_value(sym, no); + } + return false; + default: + ; + } + + if (!sym_string_within_range(sym, newval)) + return false; + + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; + sym_set_changed(sym); + } + + oldval = sym->def[S_DEF_USER].val; + size = strlen(newval) + 1; + if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { + size += 2; + sym->def[S_DEF_USER].val = val = xmalloc(size); + *val++ = '0'; + *val++ = 'x'; + } else if (!oldval || strcmp(oldval, newval)) + sym->def[S_DEF_USER].val = val = xmalloc(size); + else + return true; + + strcpy(val, newval); + free((void *)oldval); + sym_clear_all_valid(); + + return true; +} + +/* + * Find the default value associated to a symbol. + * For tristate symbol handle the modules=n case + * in which case "m" becomes "y". + * If the symbol does not have any default then fallback + * to the fixed default values. + */ +const char *sym_get_string_default(struct symbol *sym) +{ + struct property *prop; + struct symbol *ds; + const char *str; + tristate val; + + sym_calc_visibility(sym); + sym_calc_value(modules_sym); + val = symbol_no.curr.tri; + str = symbol_empty.curr.val; + + /* If symbol has a default value look it up */ + prop = sym_get_default_prop(sym); + if (prop != NULL) { + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + /* The visibility may limit the value from yes => mod */ + val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); + break; + default: + /* + * The following fails to handle the situation + * where a default value is further limited by + * the valid range. + */ + ds = prop_get_symbol(prop); + if (ds != NULL) { + sym_calc_value(ds); + str = (const char *)ds->curr.val; + } + } + } + + /* Handle select statements */ + val = EXPR_OR(val, sym->rev_dep.tri); + + /* transpose mod to yes if modules are not enabled */ + if (val == mod) + if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no) + val = yes; + + /* transpose mod to yes if type is bool */ + if (sym->type == S_BOOLEAN && val == mod) + val = yes; + + /* adjust the default value if this symbol is implied by another */ + if (val < sym->implied.tri) + val = sym->implied.tri; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (val) { + case no: return "n"; + case mod: return "m"; + case yes: return "y"; + } + case S_INT: + case S_HEX: + return str; + case S_STRING: + return str; + case S_OTHER: + case S_UNKNOWN: + break; + } + return ""; +} + +const char *sym_get_string_value(struct symbol *sym) +{ + tristate val; + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + val = sym_get_tristate_value(sym); + switch (val) { + case no: + return "n"; + case mod: + sym_calc_value(modules_sym); + return (modules_sym->curr.tri == no) ? "n" : "m"; + case yes: + return "y"; + } + break; + default: + ; + } + return (const char *)sym->curr.val; +} + +bool sym_is_changable(struct symbol *sym) +{ + return sym->visible > sym->rev_dep.tri; +} + +static unsigned strhash(const char *s) +{ + /* fnv32 hash */ + unsigned hash = 2166136261U; + for (; *s; s++) + hash = (hash ^ *s) * 0x01000193; + return hash; +} + +struct symbol *sym_lookup(const char *name, int flags) +{ + struct symbol *symbol; + char *new_name; + int hash; + + if (name) { + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + (flags ? symbol->flags & flags + : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE)))) + return symbol; + } + new_name = strdup(name); + } else { + new_name = NULL; + hash = 0; + } + + symbol = xmalloc(sizeof(*symbol)); + memset(symbol, 0, sizeof(*symbol)); + symbol->name = new_name; + symbol->type = S_UNKNOWN; + symbol->flags |= flags; + + symbol->next = symbol_hash[hash]; + symbol_hash[hash] = symbol; + + return symbol; +} + +struct symbol *sym_find(const char *name) +{ + struct symbol *symbol = NULL; + int hash = 0; + + if (!name) + return NULL; + + if (name[0] && !name[1]) { + switch (name[0]) { + case 'y': return &symbol_yes; + case 'm': return &symbol_mod; + case 'n': return &symbol_no; + } + } + hash = strhash(name) % SYMBOL_HASHSIZE; + + for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { + if (symbol->name && + !strcmp(symbol->name, name) && + !(symbol->flags & SYMBOL_CONST)) + break; + } + + return symbol; +} + +/* + * Expand symbol's names embedded in the string given in argument. Symbols' + * name to be expanded shall be prefixed by a '$'. Unknown symbol expands to + * the empty string. + */ +const char *sym_expand_string_value(const char *in) +{ + const char *src; + char *res; + size_t reslen; + + reslen = strlen(in) + 1; + res = xmalloc(reslen); + res[0] = '\0'; + + while ((src = strchr(in, '$'))) { + char *p, name[SYMBOL_MAXLENGTH]; + const char *symval = ""; + struct symbol *sym; + size_t newlen; + + strncat(res, in, src - in); + src++; + + p = name; + while (isalnum(*src) || *src == '_') + *p++ = *src++; + *p = '\0'; + + sym = sym_find(name); + if (sym != NULL) { + sym_calc_value(sym); + symval = sym_get_string_value(sym); + } + + newlen = strlen(res) + strlen(symval) + strlen(src) + 1; + if (newlen > reslen) { + reslen = newlen; + res = realloc(res, reslen); + } + + strcat(res, symval); + in = src; + } + strcat(res, in); + + return res; +} + +const char *sym_escape_string_value(const char *in) +{ + const char *p; + size_t reslen; + char *res; + size_t l; + + reslen = strlen(in) + strlen("\"\"") + 1; + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + p += l; + + if (p[0] == '\0') + break; + + reslen++; + p++; + } + + res = xmalloc(reslen); + res[0] = '\0'; + + strcat(res, "\""); + + p = in; + for (;;) { + l = strcspn(p, "\"\\"); + strncat(res, p, l); + p += l; + + if (p[0] == '\0') + break; + + strcat(res, "\\"); + strncat(res, p++, 1); + } + + strcat(res, "\""); + return res; +} + +struct sym_match { + struct symbol *sym; + off_t so, eo; +}; + +/* Compare matched symbols as thus: + * - first, symbols that match exactly + * - then, alphabetical sort + */ +static int sym_rel_comp(const void *sym1, const void *sym2) +{ + const struct sym_match *s1 = sym1; + const struct sym_match *s2 = sym2; + int exact1, exact2; + + /* Exact match: + * - if matched length on symbol s1 is the length of that symbol, + * then this symbol should come first; + * - if matched length on symbol s2 is the length of that symbol, + * then this symbol should come first. + * Note: since the search can be a regexp, both symbols may match + * exactly; if this is the case, we can't decide which comes first, + * and we fallback to sorting alphabetically. + */ + exact1 = (s1->eo - s1->so) == strlen(s1->sym->name); + exact2 = (s2->eo - s2->so) == strlen(s2->sym->name); + if (exact1 && !exact2) + return -1; + if (!exact1 && exact2) + return 1; + + /* As a fallback, sort symbols alphabetically */ + return strcmp(s1->sym->name, s2->sym->name); +} + +struct symbol **sym_re_search(const char *pattern) +{ + struct symbol *sym, **sym_arr = NULL; + struct sym_match *sym_match_arr = NULL; + int i, cnt, size; + regex_t re; + regmatch_t match[1]; + + cnt = size = 0; + /* Skip if empty */ + if (strlen(pattern) == 0) + return NULL; + if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE)) + return NULL; + + for_all_symbols(i, sym) { + if (sym->flags & SYMBOL_CONST || !sym->name) + continue; + if (regexec(&re, sym->name, 1, match, 0)) + continue; + if (cnt >= size) { + void *tmp; + size += 16; + tmp = realloc(sym_match_arr, size * sizeof(struct sym_match)); + if (!tmp) + goto sym_re_search_free; + sym_match_arr = tmp; + } + sym_calc_value(sym); + /* As regexec returned 0, we know we have a match, so + * we can use match[0].rm_[se]o without further checks + */ + sym_match_arr[cnt].so = match[0].rm_so; + sym_match_arr[cnt].eo = match[0].rm_eo; + sym_match_arr[cnt++].sym = sym; + } + if (sym_match_arr) { + qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp); + sym_arr = malloc((cnt+1) * sizeof(struct symbol)); + if (!sym_arr) + goto sym_re_search_free; + for (i = 0; i < cnt; i++) + sym_arr[i] = sym_match_arr[i].sym; + sym_arr[cnt] = NULL; + } +sym_re_search_free: + /* sym_match_arr can be NULL if no match, but free(NULL) is OK */ + free(sym_match_arr); + regfree(&re); + + return sym_arr; +} + +/* + * When we check for recursive dependencies we use a stack to save + * current state so we can print out relevant info to user. + * The entries are located on the call stack so no need to free memory. + * Note insert() remove() must always match to properly clear the stack. + */ +static struct dep_stack { + struct dep_stack *prev, *next; + struct symbol *sym; + struct property *prop; + struct expr *expr; +} *check_top; + +static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) +{ + memset(stack, 0, sizeof(*stack)); + if (check_top) + check_top->next = stack; + stack->prev = check_top; + stack->sym = sym; + check_top = stack; +} + +static void dep_stack_remove(void) +{ + check_top = check_top->prev; + if (check_top) + check_top->next = NULL; +} + +/* + * Called when we have detected a recursive dependency. + * check_top point to the top of the stact so we use + * the ->prev pointer to locate the bottom of the stack. + */ +static void sym_check_print_recursive(struct symbol *last_sym) +{ + struct dep_stack *stack; + struct symbol *sym, *next_sym; + struct menu *menu = NULL; + struct property *prop; + struct dep_stack cv_stack; + + if (sym_is_choice_value(last_sym)) { + dep_stack_insert(&cv_stack, last_sym); + last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); + } + + for (stack = check_top; stack != NULL; stack = stack->prev) + if (stack->sym == last_sym) + break; + if (!stack) { + fprintf(stderr, "unexpected recursive dependency error\n"); + return; + } + + for (; stack; stack = stack->next) { + sym = stack->sym; + next_sym = stack->next ? stack->next->sym : last_sym; + prop = stack->prop; + if (prop == NULL) + prop = stack->sym->prop; + + /* for choice values find the menu entry (used below) */ + if (sym_is_choice(sym) || sym_is_choice_value(sym)) { + for (prop = sym->prop; prop; prop = prop->next) { + menu = prop->menu; + if (prop->menu) + break; + } + } + if (stack->sym == last_sym) + fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", + prop->file->name, prop->lineno); + fprintf(stderr, "For a resolution refer to Documentation/kbuild/kconfig-language.txt\n"); + fprintf(stderr, "subsection \"Kconfig recursive dependency limitations\"\n"); + if (stack->expr) { + fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "", + prop_get_type_name(prop->type), + next_sym->name ? next_sym->name : ""); + } else if (stack->prop) { + fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "", + next_sym->name ? next_sym->name : ""); + } else if (sym_is_choice(sym)) { + fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "", + next_sym->name ? next_sym->name : ""); + } else if (sym_is_choice_value(sym)) { + fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", + menu->file->name, menu->lineno, + sym->name ? sym->name : "", + next_sym->name ? next_sym->name : ""); + } else { + fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", + prop->file->name, prop->lineno, + sym->name ? sym->name : "", + next_sym->name ? next_sym->name : ""); + } + } + + if (check_top == &cv_stack) + dep_stack_remove(); +} + +static struct symbol *sym_check_expr_deps(struct expr *e) +{ + struct symbol *sym; + + if (!e) + return NULL; + switch (e->type) { + case E_OR: + case E_AND: + sym = sym_check_expr_deps(e->left.expr); + if (sym) + return sym; + return sym_check_expr_deps(e->right.expr); + case E_NOT: + return sym_check_expr_deps(e->left.expr); + case E_EQUAL: + case E_GEQ: + case E_GTH: + case E_LEQ: + case E_LTH: + case E_UNEQUAL: + sym = sym_check_deps(e->left.sym); + if (sym) + return sym; + return sym_check_deps(e->right.sym); + case E_SYMBOL: + return sym_check_deps(e->left.sym); + default: + break; + } + printf("Oops! How to check %d?\n", e->type); + return NULL; +} + +/* return NULL when dependencies are OK */ +static struct symbol *sym_check_sym_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + struct dep_stack stack; + + dep_stack_insert(&stack, sym); + + sym2 = sym_check_expr_deps(sym->rev_dep.expr); + if (sym2) + goto out; + + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->type == P_CHOICE || prop->type == P_SELECT) + continue; + stack.prop = prop; + sym2 = sym_check_expr_deps(prop->visible.expr); + if (sym2) + break; + if (prop->type != P_DEFAULT || sym_is_choice(sym)) + continue; + stack.expr = prop->expr; + sym2 = sym_check_expr_deps(prop->expr); + if (sym2) + break; + stack.expr = NULL; + } + +out: + dep_stack_remove(); + + return sym2; +} + +static struct symbol *sym_check_choice_deps(struct symbol *choice) +{ + struct symbol *sym, *sym2; + struct property *prop; + struct expr *e; + struct dep_stack stack; + + dep_stack_insert(&stack, choice); + + prop = sym_get_choice_prop(choice); + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + + choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(choice); + choice->flags &= ~SYMBOL_CHECK; + if (sym2) + goto out; + + expr_list_for_each_sym(prop->expr, e, sym) { + sym2 = sym_check_sym_deps(sym); + if (sym2) + break; + } +out: + expr_list_for_each_sym(prop->expr, e, sym) + sym->flags &= ~SYMBOL_CHECK; + + if (sym2 && sym_is_choice_value(sym2) && + prop_get_symbol(sym_get_choice_prop(sym2)) == choice) + sym2 = choice; + + dep_stack_remove(); + + return sym2; +} + +struct symbol *sym_check_deps(struct symbol *sym) +{ + struct symbol *sym2; + struct property *prop; + + if (sym->flags & SYMBOL_CHECK) { + sym_check_print_recursive(sym); + return sym; + } + if (sym->flags & SYMBOL_CHECKED) + return NULL; + + if (sym_is_choice_value(sym)) { + struct dep_stack stack; + + /* for choice groups start the check with main choice symbol */ + dep_stack_insert(&stack, sym); + prop = sym_get_choice_prop(sym); + sym2 = sym_check_deps(prop_get_symbol(prop)); + dep_stack_remove(); + } else if (sym_is_choice(sym)) { + sym2 = sym_check_choice_deps(sym); + } else { + sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); + sym2 = sym_check_sym_deps(sym); + sym->flags &= ~SYMBOL_CHECK; + } + + if (sym2 && sym2 == sym) + sym2 = NULL; + + return sym2; +} + +struct property *prop_alloc(enum prop_type type, struct symbol *sym) +{ + struct property *prop; + struct property **propp; + + prop = xmalloc(sizeof(*prop)); + memset(prop, 0, sizeof(*prop)); + prop->type = type; + prop->sym = sym; + prop->file = current_file; + prop->lineno = zconf_lineno(); + + /* append property to the prop list of symbol */ + if (sym) { + for (propp = &sym->prop; *propp; propp = &(*propp)->next) + ; + *propp = prop; + } + + return prop; +} + +struct symbol *prop_get_symbol(struct property *prop) +{ + if (prop->expr && (prop->expr->type == E_SYMBOL || + prop->expr->type == E_LIST)) + return prop->expr->left.sym; + return NULL; +} + +const char *prop_get_type_name(enum prop_type type) +{ + switch (type) { + case P_PROMPT: + return "prompt"; + case P_ENV: + return "env"; + case P_COMMENT: + return "comment"; + case P_MENU: + return "menu"; + case P_DEFAULT: + return "default"; + case P_CHOICE: + return "choice"; + case P_SELECT: + return "select"; + case P_IMPLY: + return "imply"; + case P_RANGE: + return "range"; + case P_SYMBOL: + return "symbol"; + case P_UNKNOWN: + break; + } + return "unknown"; +} + +static void prop_add_env(const char *env) +{ + struct symbol *sym, *sym2; + struct property *prop; + char *p; + + sym = current_entry->sym; + sym->flags |= SYMBOL_AUTO; + for_all_properties(sym, prop, P_ENV) { + sym2 = prop_get_symbol(prop); + if (strcmp(sym2->name, env)) + menu_warn(current_entry, "redefining environment symbol from %s", + sym2->name); + return; + } + + prop = prop_alloc(P_ENV, sym); + prop->expr = expr_alloc_symbol(sym_lookup(env, SYMBOL_CONST)); + + sym_env_list = expr_alloc_one(E_LIST, sym_env_list); + sym_env_list->right.sym = sym; + + p = getenv(env); + if (p) + sym_add_default(sym, p); + else + menu_warn(current_entry, "environment variable %s undefined", env); +} diff --git a/tools/kconfig/util.c b/tools/kconfig/util.c new file mode 100644 index 0000000..0e76042 --- /dev/null +++ b/tools/kconfig/util.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2002-2005 Roman Zippel + * Copyright (C) 2002-2005 Sam Ravnborg + * + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include "lkc.h" + +/* file already present in list? If not add it */ +struct file *file_lookup(const char *name) +{ + struct file *file; + const char *file_name = sym_expand_string_value(name); + + for (file = file_list; file; file = file->next) { + if (!strcmp(name, file->name)) { + free((void *)file_name); + return file; + } + } + + file = xmalloc(sizeof(*file)); + memset(file, 0, sizeof(*file)); + file->name = file_name; + file->next = file_list; + file_list = file; + return file; +} + +/* write a dependency file as used by kbuild to track dependencies */ +int file_write_dep(const char *name) +{ + struct symbol *sym, *env_sym; + struct expr *e; + struct file *file; + FILE *out; + + if (!name) + name = ".kconfig.d"; + out = fopen("..config.tmp", "w"); + if (!out) + return 1; + fprintf(out, "deps_config := \\\n"); + for (file = file_list; file; file = file->next) { + if (file->next) + fprintf(out, "\t%s \\\n", file->name); + else + fprintf(out, "\t%s\n", file->name); + } + fprintf(out, "\n%s: \\\n" + "\t$(deps_config)\n\n", conf_get_autoconfig_name()); + + expr_list_for_each_sym(sym_env_list, e, sym) { + struct property *prop; + const char *value; + + prop = sym_get_env_prop(sym); + env_sym = prop_get_symbol(prop); + if (!env_sym) + continue; + value = getenv(env_sym->name); + if (!value) + value = ""; + fprintf(out, "ifneq \"$(%s)\" \"%s\"\n", env_sym->name, value); + fprintf(out, "%s: FORCE\n", conf_get_autoconfig_name()); + fprintf(out, "endif\n"); + } + + fprintf(out, "\n$(deps_config): ;\n"); + fclose(out); + rename("..config.tmp", name); + return 0; +} + + +/* Allocate initial growable string */ +struct gstr str_new(void) +{ + struct gstr gs; + gs.s = xmalloc(sizeof(char) * 64); + gs.len = 64; + gs.max_width = 0; + strcpy(gs.s, "\0"); + return gs; +} + +/* Free storage for growable string */ +void str_free(struct gstr *gs) +{ + if (gs->s) + free(gs->s); + gs->s = NULL; + gs->len = 0; +} + +/* Append to growable string */ +void str_append(struct gstr *gs, const char *s) +{ + size_t l; + if (s) { + l = strlen(gs->s) + strlen(s) + 1; + if (l > gs->len) { + gs->s = realloc(gs->s, l); + gs->len = l; + } + strcat(gs->s, s); + } +} + +/* Append printf formatted string to growable string */ +void str_printf(struct gstr *gs, const char *fmt, ...) +{ + va_list ap; + char s[10000]; /* big enough... */ + va_start(ap, fmt); + vsnprintf(s, sizeof(s), fmt, ap); + str_append(gs, s); + va_end(ap); +} + +/* Retrieve value of growable string */ +const char *str_get(struct gstr *gs) +{ + return gs->s; +} + +void *xmalloc(size_t size) +{ + void *p = malloc(size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} + +void *xcalloc(size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} diff --git a/tools/kconfig/zconf.gperf b/tools/kconfig/zconf.gperf new file mode 100644 index 0000000..ead02ed --- /dev/null +++ b/tools/kconfig/zconf.gperf @@ -0,0 +1,50 @@ +%language=ANSI-C +%define hash-function-name kconf_id_hash +%define lookup-function-name kconf_id_lookup +%define string-pool-name kconf_id_strings +%compare-strncmp +%enum +%pic +%struct-type + +struct kconf_id; + +static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len); + +%% +mainmenu, T_MAINMENU, TF_COMMAND +menu, T_MENU, TF_COMMAND +endmenu, T_ENDMENU, TF_COMMAND +source, T_SOURCE, TF_COMMAND +choice, T_CHOICE, TF_COMMAND +endchoice, T_ENDCHOICE, TF_COMMAND +comment, T_COMMENT, TF_COMMAND +config, T_CONFIG, TF_COMMAND +menuconfig, T_MENUCONFIG, TF_COMMAND +help, T_HELP, TF_COMMAND +---help---, T_HELP, TF_COMMAND +if, T_IF, TF_COMMAND|TF_PARAM +endif, T_ENDIF, TF_COMMAND +depends, T_DEPENDS, TF_COMMAND +optional, T_OPTIONAL, TF_COMMAND +default, T_DEFAULT, TF_COMMAND, S_UNKNOWN +prompt, T_PROMPT, TF_COMMAND +tristate, T_TYPE, TF_COMMAND, S_TRISTATE +def_tristate, T_DEFAULT, TF_COMMAND, S_TRISTATE +bool, T_TYPE, TF_COMMAND, S_BOOLEAN +boolean, T_TYPE, TF_COMMAND, S_BOOLEAN +def_bool, T_DEFAULT, TF_COMMAND, S_BOOLEAN +int, T_TYPE, TF_COMMAND, S_INT +hex, T_TYPE, TF_COMMAND, S_HEX +string, T_TYPE, TF_COMMAND, S_STRING +select, T_SELECT, TF_COMMAND +imply, T_IMPLY, TF_COMMAND +range, T_RANGE, TF_COMMAND +visible, T_VISIBLE, TF_COMMAND +option, T_OPTION, TF_COMMAND +on, T_ON, TF_PARAM +modules, T_OPT_MODULES, TF_OPTION +defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION +env, T_OPT_ENV, TF_OPTION +allnoconfig_y, T_OPT_ALLNOCONFIG_Y,TF_OPTION +%% diff --git a/tools/kconfig/zconf.hash.c_shipped b/tools/kconfig/zconf.hash.c_shipped new file mode 100644 index 0000000..d51b15d --- /dev/null +++ b/tools/kconfig/zconf.hash.c_shipped @@ -0,0 +1,297 @@ +/* ANSI-C code produced by gperf version 3.0.4 */ +/* Command-line: gperf -t --output-file scripts/kconfig/zconf.hash.c_shipped -a -C -E -g -k '1,3,$' -p -t scripts/kconfig/zconf.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +#line 10 "scripts/kconfig/zconf.gperf" +struct kconf_id; + +static const struct kconf_id *kconf_id_lookup(register const char *str, register unsigned int len); +/* maximum key range = 71, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +kconf_id_hash (register const char *str, register unsigned int len) +{ + static const unsigned char asso_values[] = + { + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 0, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 10, 25, 25, + 0, 0, 0, 5, 0, 0, 73, 73, 5, 0, + 10, 5, 45, 73, 20, 20, 0, 15, 15, 73, + 20, 0, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 73, 73, 73, 73, 73, 73 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval + asso_values[(unsigned char)str[len - 1]]; +} + +struct kconf_id_strings_t + { + char kconf_id_strings_str2[sizeof("if")]; + char kconf_id_strings_str3[sizeof("int")]; + char kconf_id_strings_str5[sizeof("endif")]; + char kconf_id_strings_str7[sizeof("default")]; + char kconf_id_strings_str8[sizeof("tristate")]; + char kconf_id_strings_str9[sizeof("endchoice")]; + char kconf_id_strings_str10[sizeof("---help---")]; + char kconf_id_strings_str12[sizeof("def_tristate")]; + char kconf_id_strings_str13[sizeof("def_bool")]; + char kconf_id_strings_str14[sizeof("defconfig_list")]; + char kconf_id_strings_str17[sizeof("on")]; + char kconf_id_strings_str18[sizeof("optional")]; + char kconf_id_strings_str21[sizeof("option")]; + char kconf_id_strings_str22[sizeof("endmenu")]; + char kconf_id_strings_str23[sizeof("mainmenu")]; + char kconf_id_strings_str25[sizeof("menuconfig")]; + char kconf_id_strings_str27[sizeof("modules")]; + char kconf_id_strings_str28[sizeof("allnoconfig_y")]; + char kconf_id_strings_str29[sizeof("menu")]; + char kconf_id_strings_str31[sizeof("select")]; + char kconf_id_strings_str32[sizeof("comment")]; + char kconf_id_strings_str33[sizeof("env")]; + char kconf_id_strings_str35[sizeof("range")]; + char kconf_id_strings_str36[sizeof("choice")]; + char kconf_id_strings_str39[sizeof("bool")]; + char kconf_id_strings_str41[sizeof("source")]; + char kconf_id_strings_str42[sizeof("visible")]; + char kconf_id_strings_str43[sizeof("hex")]; + char kconf_id_strings_str46[sizeof("config")]; + char kconf_id_strings_str47[sizeof("boolean")]; + char kconf_id_strings_str50[sizeof("imply")]; + char kconf_id_strings_str51[sizeof("string")]; + char kconf_id_strings_str54[sizeof("help")]; + char kconf_id_strings_str56[sizeof("prompt")]; + char kconf_id_strings_str72[sizeof("depends")]; + }; +static const struct kconf_id_strings_t kconf_id_strings_contents = + { + "if", + "int", + "endif", + "default", + "tristate", + "endchoice", + "---help---", + "def_tristate", + "def_bool", + "defconfig_list", + "on", + "optional", + "option", + "endmenu", + "mainmenu", + "menuconfig", + "modules", + "allnoconfig_y", + "menu", + "select", + "comment", + "env", + "range", + "choice", + "bool", + "source", + "visible", + "hex", + "config", + "boolean", + "imply", + "string", + "help", + "prompt", + "depends" + }; +#define kconf_id_strings ((const char *) &kconf_id_strings_contents) +#ifdef __GNUC__ +__inline +#if defined __GNUC_STDC_INLINE__ || defined __GNUC_GNU_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const struct kconf_id * +kconf_id_lookup (register const char *str, register unsigned int len) +{ + enum + { + TOTAL_KEYWORDS = 35, + MIN_WORD_LENGTH = 2, + MAX_WORD_LENGTH = 14, + MIN_HASH_VALUE = 2, + MAX_HASH_VALUE = 72 + }; + + static const struct kconf_id wordlist[] = + { + {-1}, {-1}, +#line 26 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, +#line 37 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, + {-1}, +#line 27 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, + {-1}, +#line 30 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, +#line 32 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, +#line 20 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, +#line 25 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_HELP, TF_COMMAND}, + {-1}, +#line 33 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_TRISTATE}, +#line 36 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, +#line 47 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_OPT_DEFCONFIG_LIST,TF_OPTION}, + {-1}, {-1}, +#line 45 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_ON, TF_PARAM}, +#line 29 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_OPTIONAL, TF_COMMAND}, + {-1}, {-1}, +#line 44 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_OPTION, TF_COMMAND}, +#line 17 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ENDMENU, TF_COMMAND}, +#line 15 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_MAINMENU, TF_COMMAND}, + {-1}, +#line 23 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str25, T_MENUCONFIG, TF_COMMAND}, + {-1}, +#line 46 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION}, +#line 49 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPT_ALLNOCONFIG_Y,TF_OPTION}, +#line 16 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND}, + {-1}, +#line 40 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, +#line 21 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, +#line 48 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_OPT_ENV, TF_OPTION}, + {-1}, +#line 42 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35, T_RANGE, TF_COMMAND}, +#line 19 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36, T_CHOICE, TF_COMMAND}, + {-1}, {-1}, +#line 34 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str39, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {-1}, +#line 18 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_SOURCE, TF_COMMAND}, +#line 43 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str42, T_VISIBLE, TF_COMMAND}, +#line 38 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str43, T_TYPE, TF_COMMAND, S_HEX}, + {-1}, {-1}, +#line 22 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_CONFIG, TF_COMMAND}, +#line 35 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str47, T_TYPE, TF_COMMAND, S_BOOLEAN}, + {-1}, {-1}, +#line 41 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str50, T_IMPLY, TF_COMMAND}, +#line 39 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str51, T_TYPE, TF_COMMAND, S_STRING}, + {-1}, {-1}, +#line 24 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str54, T_HELP, TF_COMMAND}, + {-1}, +#line 31 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str56, T_PROMPT, TF_COMMAND}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, + {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, +#line 28 "scripts/kconfig/zconf.gperf" + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str72, T_DEPENDS, TF_COMMAND} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = kconf_id_hash (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register int o = wordlist[key].name; + if (o >= 0) + { + register const char *s = o + kconf_id_strings; + + if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0') + return &wordlist[key]; + } + } + } + return 0; +} +#line 50 "scripts/kconfig/zconf.gperf" + diff --git a/tools/kconfig/zconf.l b/tools/kconfig/zconf.l new file mode 100644 index 0000000..c410d25 --- /dev/null +++ b/tools/kconfig/zconf.l @@ -0,0 +1,374 @@ +%option nostdinit noyywrap never-interactive full ecs +%option 8bit nodefault perf-report perf-report +%option noinput +%x COMMAND HELP STRING PARAM +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = xmalloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = xmalloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +static void warn_ignored_character(char chr) +{ + fprintf(stderr, + "%s:%d:warning: ignoring unsupported character '%c'\n", + zconf_curname(), zconf_lineno(), chr); +} +%} + +n [A-Za-z0-9_-] + +%% + int str = 0; + int ts, i; + +[ \t]*#.*\n | +[ \t]*\n { + current_file->lineno++; + return T_EOL; +} +[ \t]*#.* + + +[ \t]+ { + BEGIN(COMMAND); +} + +. { + unput(yytext[0]); + BEGIN(COMMAND); +} + + +{ + {n}+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + . warn_ignored_character(*yytext); + \n { + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } +} + +{ + "&&" return T_AND; + "||" return T_OR; + "(" return T_OPEN_PAREN; + ")" return T_CLOSE_PAREN; + "!" return T_NOT; + "=" return T_EQUAL; + "!=" return T_UNEQUAL; + "<=" return T_LESS_EQUAL; + ">=" return T_GREATER_EQUAL; + "<" return T_LESS; + ">" return T_GREATER; + \"|\' { + str = yytext[0]; + new_string(); + BEGIN(STRING); + } + \n BEGIN(INITIAL); current_file->lineno++; return T_EOL; + ({n}|[/.])+ { + const struct kconf_id *id = kconf_id_lookup(yytext, yyleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(yytext, yyleng); + zconflval.string = text; + return T_WORD; + } + #.* /* comment */ + \\\n current_file->lineno++; + [[:blank:]]+ + . warn_ignored_character(*yytext); + <> { + BEGIN(INITIAL); + } +} + +{ + [^'"\\\n]+/\n { + append_string(yytext, yyleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + [^'"\\\n]+ { + append_string(yytext, yyleng); + } + \\.?/\n { + append_string(yytext + 1, yyleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + \\.? { + append_string(yytext + 1, yyleng - 1); + } + \'|\" { + if (str == yytext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(yytext, 1); + } + \n { + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + <> { + BEGIN(INITIAL); + } +} + +{ + [ \t]+ { + ts = 0; + for (i = 0; i < yyleng; i++) { + if (yytext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + [ \t]*\n/[^ \t\n] { + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + [ \t]*\n { + current_file->lineno++; + append_string("\n", 1); + } + [^ \t\n].* { + while (yyleng) { + if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t')) + break; + yyleng--; + } + append_string(yytext, yyleng); + if (!first_ts) + first_ts = last_ts; + } + <> { + zconf_endhelp(); + return T_HELPTEXT; + } +} + +<> { + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(yyin); + yyterminate(); +} + +%% +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + yyin = zconf_fopen(name); + if (!yyin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = xmalloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; +} + +void zconf_nextfile(const char *name) +{ + struct file *iter; + struct file *file = file_lookup(name); + struct buffer *buf = xmalloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + yyin = zconf_fopen(file->name); + if (!yyin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + for (iter = current_file->parent; iter; iter = iter->parent ) { + if (!strcmp(current_file->name,iter->name) ) { + printf("%s:%d: recursive inclusion detected. " + "Inclusion path:\n current file : '%s'\n", + zconf_curname(), zconf_lineno(), + zconf_curname()); + iter = current_file->parent; + while (iter && \ + strcmp(iter->name,current_file->name)) { + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno-1); + iter = iter->parent; + } + if (iter) + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno+1); + exit(1); + } + } + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(yyin); + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : ""; +} diff --git a/tools/kconfig/zconf.lex.c_shipped b/tools/kconfig/zconf.lex.c_shipped new file mode 100644 index 0000000..37fdf61 --- /dev/null +++ b/tools/kconfig/zconf.lex.c_shipped @@ -0,0 +1,2473 @@ + +#line 3 "scripts/kconfig/zconf.lex.c_shipped" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer zconf_create_buffer +#define yy_delete_buffer zconf_delete_buffer +#define yy_flex_debug zconf_flex_debug +#define yy_init_buffer zconf_init_buffer +#define yy_flush_buffer zconf_flush_buffer +#define yy_load_buffer_state zconf_load_buffer_state +#define yy_switch_to_buffer zconf_switch_to_buffer +#define yyin zconfin +#define yyleng zconfleng +#define yylex zconflex +#define yylineno zconflineno +#define yyout zconfout +#define yyrestart zconfrestart +#define yytext zconftext +#define yywrap zconfwrap +#define yyalloc zconfalloc +#define yyrealloc zconfrealloc +#define yyfree zconffree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE zconfrestart(zconfin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int zconfleng; + +extern FILE *zconfin, *zconfout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up zconftext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via zconfrestart()), so that the user can continue scanning by + * just pointing zconfin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when zconftext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int zconfleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow zconfwrap()'s to do buffer switches + * instead of setting up a fresh zconfin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void zconfrestart (FILE *input_file ); +void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size ); +void zconf_delete_buffer (YY_BUFFER_STATE b ); +void zconf_flush_buffer (YY_BUFFER_STATE b ); +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void zconfpop_buffer_state (void ); + +static void zconfensure_buffer_stack (void ); +static void zconf_load_buffer_state (void ); +static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len ); + +void *zconfalloc (yy_size_t ); +void *zconfrealloc (void *,yy_size_t ); +void zconffree (void * ); + +#define yy_new_buffer zconf_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + zconfensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + zconf_create_buffer(zconfin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define zconfwrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0; + +typedef int yy_state_type; + +extern int zconflineno; + +int zconflineno = 1; + +extern char *zconftext; +#define yytext_ptr zconftext +static yyconst flex_int16_t yy_nxt[][18] = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 12, 13, 14, 12, 12, 15, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 18, 16, 16, 16, 16, 16, 16 + }, + + { + 11, 16, 16, 17, 16, 16, 16, 16, 16, 16, + 16, 18, 16, 16, 16, 16, 16, 16 + + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 19, 20, 21, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 22, 22, 23, 22, 24, 22, 22, 24, 22, + 22, 22, 22, 22, 22, 22, 25, 22 + }, + + { + 11, 26, 27, 28, 29, 30, 31, 32, 30, 33, + 34, 35, 35, 36, 37, 38, 39, 40 + + }, + + { + 11, 26, 27, 28, 29, 30, 31, 32, 30, 33, + 34, 35, 35, 36, 37, 38, 39, 40 + }, + + { + -11, -11, -11, -11, -11, -11, -11, -11, -11, -11, + -11, -11, -11, -11, -11, -11, -11, -11 + }, + + { + 11, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12 + }, + + { + 11, -13, 41, 42, -13, -13, 43, -13, -13, -13, + -13, -13, -13, -13, -13, -13, -13, -13 + }, + + { + 11, -14, -14, -14, -14, -14, -14, -14, -14, -14, + -14, -14, -14, -14, -14, -14, -14, -14 + + }, + + { + 11, 44, 44, 45, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44 + }, + + { + 11, -16, -16, -16, -16, -16, -16, -16, -16, -16, + -16, -16, -16, -16, -16, -16, -16, -16 + }, + + { + 11, -17, -17, -17, -17, -17, -17, -17, -17, -17, + -17, -17, -17, -17, -17, -17, -17, -17 + }, + + { + 11, -18, -18, -18, -18, -18, -18, -18, -18, -18, + -18, 46, -18, -18, -18, -18, -18, -18 + }, + + { + 11, 47, 47, -19, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47 + + }, + + { + 11, -20, 48, 49, -20, -20, -20, -20, -20, -20, + -20, -20, -20, -20, -20, -20, -20, -20 + }, + + { + 11, 50, -21, -21, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50 + }, + + { + 11, 51, 51, 52, 51, -22, 51, 51, -22, 51, + 51, 51, 51, 51, 51, 51, -22, 51 + }, + + { + 11, -23, -23, -23, -23, -23, -23, -23, -23, -23, + -23, -23, -23, -23, -23, -23, -23, -23 + }, + + { + 11, -24, -24, -24, -24, -24, -24, -24, -24, -24, + -24, -24, -24, -24, -24, -24, -24, -24 + + }, + + { + 11, 53, 53, 54, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53 + }, + + { + 11, -26, -26, -26, -26, -26, -26, -26, -26, -26, + -26, -26, -26, -26, -26, -26, -26, -26 + }, + + { + 11, -27, 55, -27, -27, -27, -27, -27, -27, -27, + -27, -27, -27, -27, -27, -27, -27, -27 + }, + + { + 11, -28, -28, -28, -28, -28, -28, -28, -28, -28, + -28, -28, -28, -28, -28, -28, -28, -28 + }, + + { + 11, -29, -29, -29, -29, -29, -29, -29, -29, -29, + -29, -29, -29, -29, 56, -29, -29, -29 + + }, + + { + 11, -30, -30, -30, -30, -30, -30, -30, -30, -30, + -30, -30, -30, -30, -30, -30, -30, -30 + }, + + { + 11, 57, 57, -31, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57 + }, + + { + 11, -32, -32, -32, -32, -32, -32, 58, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32 + }, + + { + 11, -33, -33, -33, -33, -33, -33, -33, -33, -33, + -33, -33, -33, -33, -33, -33, -33, -33 + }, + + { + 11, -34, -34, -34, -34, -34, -34, -34, -34, -34, + -34, -34, -34, -34, -34, -34, -34, -34 + + }, + + { + 11, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, 59, 59, -35, -35, -35, -35, -35 + }, + + { + 11, -36, -36, -36, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, 60, -36, -36, -36 + }, + + { + 11, -37, -37, -37, -37, -37, -37, -37, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -37 + }, + + { + 11, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, 61, -38, -38, -38 + }, + + { + 11, -39, -39, 62, -39, -39, -39, -39, -39, -39, + -39, -39, -39, -39, -39, -39, -39, -39 + + }, + + { + 11, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -40, -40, 63 + }, + + { + 11, -41, 41, 42, -41, -41, 43, -41, -41, -41, + -41, -41, -41, -41, -41, -41, -41, -41 + }, + + { + 11, -42, -42, -42, -42, -42, -42, -42, -42, -42, + -42, -42, -42, -42, -42, -42, -42, -42 + }, + + { + 11, 44, 44, 45, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44 + }, + + { + 11, 44, 44, 45, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44 + + }, + + { + 11, -45, -45, -45, -45, -45, -45, -45, -45, -45, + -45, -45, -45, -45, -45, -45, -45, -45 + }, + + { + 11, -46, -46, -46, -46, -46, -46, -46, -46, -46, + -46, 46, -46, -46, -46, -46, -46, -46 + }, + + { + 11, 47, 47, -47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47 + }, + + { + 11, -48, 48, 49, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48 + }, + + { + 11, 50, -49, -49, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50 + + }, + + { + 11, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50 + }, + + { + 11, 51, 51, 52, 51, -51, 51, 51, -51, 51, + 51, 51, 51, 51, 51, 51, -51, 51 + }, + + { + 11, -52, -52, -52, -52, -52, -52, -52, -52, -52, + -52, -52, -52, -52, -52, -52, -52, -52 + }, + + { + 11, -53, -53, 54, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53 + }, + + { + 11, -54, -54, -54, -54, -54, -54, -54, -54, -54, + -54, -54, -54, -54, -54, -54, -54, -54 + + }, + + { + 11, -55, 55, -55, -55, -55, -55, -55, -55, -55, + -55, -55, -55, -55, -55, -55, -55, -55 + }, + + { + 11, -56, -56, -56, -56, -56, -56, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -56, -56 + }, + + { + 11, 57, 57, -57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57 + }, + + { + 11, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, -58, -58, -58 + }, + + { + 11, -59, -59, -59, -59, -59, -59, -59, -59, -59, + -59, 59, 59, -59, -59, -59, -59, -59 + + }, + + { + 11, -60, -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -60, -60, -60, -60 + }, + + { + 11, -61, -61, -61, -61, -61, -61, -61, -61, -61, + -61, -61, -61, -61, -61, -61, -61, -61 + }, + + { + 11, -62, -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -62, -62, -62, -62, -62, -62 + }, + + { + 11, -63, -63, -63, -63, -63, -63, -63, -63, -63, + -63, -63, -63, -63, -63, -63, -63, -63 + }, + + } ; + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up zconftext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + zconfleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 37 +#define YY_END_OF_BUFFER 38 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[64] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 38, 5, 4, 2, 3, 7, 8, 6, 36, 33, + 35, 28, 32, 31, 30, 26, 25, 21, 13, 20, + 23, 26, 11, 12, 22, 18, 14, 19, 26, 26, + 4, 2, 3, 3, 1, 6, 36, 33, 35, 34, + 28, 27, 30, 29, 25, 15, 23, 9, 22, 16, + 17, 24, 10 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 7, 8, 9, + 10, 1, 1, 1, 11, 12, 12, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 1, 1, 13, + 14, 15, 1, 1, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 1, 16, 1, 1, 11, 1, 11, 11, 11, 11, + + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 1, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +extern int zconf_flex_debug; +int zconf_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *zconftext; +#define YY_NO_INPUT 1 + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include + +#include "lkc.h" + +#define START_STRSIZE 16 + +static struct { + struct file *file; + int lineno; +} current_pos; + +static char *text; +static int text_size, text_asize; + +struct buffer { + struct buffer *parent; + YY_BUFFER_STATE state; +}; + +struct buffer *current_buf; + +static int last_ts, first_ts; + +static void zconf_endhelp(void); +static void zconf_endfile(void); + +static void new_string(void) +{ + text = xmalloc(START_STRSIZE); + text_asize = START_STRSIZE; + text_size = 0; + *text = 0; +} + +static void append_string(const char *str, int size) +{ + int new_size = text_size + size + 1; + if (new_size > text_asize) { + new_size += START_STRSIZE - 1; + new_size &= -START_STRSIZE; + text = realloc(text, new_size); + text_asize = new_size; + } + memcpy(text + text_size, str, size); + text_size += size; + text[text_size] = 0; +} + +static void alloc_string(const char *str, int size) +{ + text = xmalloc(size + 1); + memcpy(text, str, size); + text[size] = 0; +} + +static void warn_ignored_character(char chr) +{ + fprintf(stderr, + "%s:%d:warning: ignoring unsupported character '%c'\n", + zconf_curname(), zconf_lineno(), chr); +} + +#define INITIAL 0 +#define COMMAND 1 +#define HELP 2 +#define STRING 3 +#define PARAM 4 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int zconflex_destroy (void ); + +int zconfget_debug (void ); + +void zconfset_debug (int debug_flag ); + +YY_EXTRA_TYPE zconfget_extra (void ); + +void zconfset_extra (YY_EXTRA_TYPE user_defined ); + +FILE *zconfget_in (void ); + +void zconfset_in (FILE * in_str ); + +FILE *zconfget_out (void ); + +void zconfset_out (FILE * out_str ); + +int zconfget_leng (void ); + +char *zconfget_text (void ); + +int zconfget_lineno (void ); + +void zconfset_lineno (int line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int zconfwrap (void ); +#else +extern int zconfwrap (void ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( zconftext, zconfleng, 1, zconfout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + errno=0; \ + while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(zconfin); \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int zconflex (void); + +#define YY_DECL int zconflex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after zconftext and zconfleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + int str = 0; + int ts, i; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! zconfin ) + zconfin = stdin; + + if ( ! zconfout ) + zconfout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of zconftext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)] ]) > 0 ) + ++yy_cp; + + yy_current_state = -yy_current_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ +case 1: +/* rule 1 can match eol */ +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + return T_EOL; +} + YY_BREAK +case 3: +YY_RULE_SETUP + + YY_BREAK +case 4: +YY_RULE_SETUP +{ + BEGIN(COMMAND); +} + YY_BREAK +case 5: +YY_RULE_SETUP +{ + unput(zconftext[0]); + BEGIN(COMMAND); +} + YY_BREAK + +case 6: +YY_RULE_SETUP +{ + const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + BEGIN(PARAM); + current_pos.file = current_file; + current_pos.lineno = current_file->lineno; + if (id && id->flags & TF_COMMAND) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 7: +YY_RULE_SETUP +warn_ignored_character(*zconftext); + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +{ + BEGIN(INITIAL); + current_file->lineno++; + return T_EOL; + } + YY_BREAK + +case 9: +YY_RULE_SETUP +return T_AND; + YY_BREAK +case 10: +YY_RULE_SETUP +return T_OR; + YY_BREAK +case 11: +YY_RULE_SETUP +return T_OPEN_PAREN; + YY_BREAK +case 12: +YY_RULE_SETUP +return T_CLOSE_PAREN; + YY_BREAK +case 13: +YY_RULE_SETUP +return T_NOT; + YY_BREAK +case 14: +YY_RULE_SETUP +return T_EQUAL; + YY_BREAK +case 15: +YY_RULE_SETUP +return T_UNEQUAL; + YY_BREAK +case 16: +YY_RULE_SETUP +return T_LESS_EQUAL; + YY_BREAK +case 17: +YY_RULE_SETUP +return T_GREATER_EQUAL; + YY_BREAK +case 18: +YY_RULE_SETUP +return T_LESS; + YY_BREAK +case 19: +YY_RULE_SETUP +return T_GREATER; + YY_BREAK +case 20: +YY_RULE_SETUP +{ + str = zconftext[0]; + new_string(); + BEGIN(STRING); + } + YY_BREAK +case 21: +/* rule 21 can match eol */ +YY_RULE_SETUP +BEGIN(INITIAL); current_file->lineno++; return T_EOL; + YY_BREAK +case 22: +YY_RULE_SETUP +{ + const struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng); + if (id && id->flags & TF_PARAM) { + zconflval.id = id; + return id->token; + } + alloc_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD; + } + YY_BREAK +case 23: +YY_RULE_SETUP +/* comment */ + YY_BREAK +case 24: +/* rule 24 can match eol */ +YY_RULE_SETUP +current_file->lineno++; + YY_BREAK +case 25: +YY_RULE_SETUP + + YY_BREAK +case 26: +YY_RULE_SETUP +warn_ignored_character(*zconftext); + YY_BREAK +case YY_STATE_EOF(PARAM): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 27: +/* rule 27 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 28: +YY_RULE_SETUP +{ + append_string(zconftext, zconfleng); + } + YY_BREAK +case 29: +/* rule 29 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + zconflval.string = text; + return T_WORD_QUOTE; + } + YY_BREAK +case 30: +YY_RULE_SETUP +{ + append_string(zconftext + 1, zconfleng - 1); + } + YY_BREAK +case 31: +YY_RULE_SETUP +{ + if (str == zconftext[0]) { + BEGIN(PARAM); + zconflval.string = text; + return T_WORD_QUOTE; + } else + append_string(zconftext, 1); + } + YY_BREAK +case 32: +/* rule 32 can match eol */ +YY_RULE_SETUP +{ + printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno()); + current_file->lineno++; + BEGIN(INITIAL); + return T_EOL; + } + YY_BREAK +case YY_STATE_EOF(STRING): +{ + BEGIN(INITIAL); + } + YY_BREAK + +case 33: +YY_RULE_SETUP +{ + ts = 0; + for (i = 0; i < zconfleng; i++) { + if (zconftext[i] == '\t') + ts = (ts & ~7) + 8; + else + ts++; + } + last_ts = ts; + if (first_ts) { + if (ts < first_ts) { + zconf_endhelp(); + return T_HELPTEXT; + } + ts -= first_ts; + while (ts > 8) { + append_string(" ", 8); + ts -= 8; + } + append_string(" ", ts); + } + } + YY_BREAK +case 34: +/* rule 34 can match eol */ +*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */ +(yy_c_buf_p) = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up zconftext again */ +YY_RULE_SETUP +{ + current_file->lineno++; + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK +case 35: +/* rule 35 can match eol */ +YY_RULE_SETUP +{ + current_file->lineno++; + append_string("\n", 1); + } + YY_BREAK +case 36: +YY_RULE_SETUP +{ + while (zconfleng) { + if ((zconftext[zconfleng-1] != ' ') && (zconftext[zconfleng-1] != '\t')) + break; + zconfleng--; + } + append_string(zconftext, zconfleng); + if (!first_ts) + first_ts = last_ts; + } + YY_BREAK +case YY_STATE_EOF(HELP): +{ + zconf_endhelp(); + return T_HELPTEXT; + } + YY_BREAK + +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(COMMAND): +{ + if (current_file) { + zconf_endfile(); + return T_EOL; + } + fclose(zconfin); + yyterminate(); +} + YY_BREAK +case 37: +YY_RULE_SETUP +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed zconfin at a new source and called + * zconflex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( zconfwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * zconftext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of zconflex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + zconfrestart(zconfin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) zconfrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + + yy_current_state = yy_nxt[yy_current_state][1]; + yy_is_jam = (yy_current_state <= 0); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp ) +{ + register char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up zconftext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = (yy_n_chars) + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + zconfrestart(zconfin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( zconfwrap( ) ) + return EOF; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve zconftext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void zconfrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + zconfensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + zconf_create_buffer(zconfin,YY_BUF_SIZE ); + } + + zconf_init_buffer(YY_CURRENT_BUFFER,input_file ); + zconf_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * zconfpop_buffer_state(); + * zconfpush_buffer_state(new_buffer); + */ + zconfensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + zconf_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (zconfwrap()) processing, but the only time this flag + * is looked at is after zconfwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void zconf_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE zconf_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + zconf_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with zconf_create_buffer() + * + */ + void zconf_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + zconffree((void *) b->yy_ch_buf ); + + zconffree((void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a zconfrestart() or at EOF. + */ + static void zconf_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + zconf_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then zconf_init_buffer was _probably_ + * called from zconfrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void zconf_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + zconf_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + zconfensure_buffer_stack(); + + /* This block is copied from zconf_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from zconf_switch_to_buffer. */ + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void zconfpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + zconf_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void zconfensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in zconfensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + zconf_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to zconflex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * zconf_scan_bytes() instead. + */ +YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr ) +{ + + return zconf_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) zconfalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = zconf_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up zconftext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + zconftext[zconfleng] = (yy_hold_char); \ + (yy_c_buf_p) = zconftext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + zconfleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int zconfget_lineno (void) +{ + + return zconflineno; +} + +/** Get the input stream. + * + */ +FILE *zconfget_in (void) +{ + return zconfin; +} + +/** Get the output stream. + * + */ +FILE *zconfget_out (void) +{ + return zconfout; +} + +/** Get the length of the current token. + * + */ +int zconfget_leng (void) +{ + return zconfleng; +} + +/** Get the current token. + * + */ + +char *zconfget_text (void) +{ + return zconftext; +} + +/** Set the current line number. + * @param line_number + * + */ +void zconfset_lineno (int line_number ) +{ + + zconflineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see zconf_switch_to_buffer + */ +void zconfset_in (FILE * in_str ) +{ + zconfin = in_str ; +} + +void zconfset_out (FILE * out_str ) +{ + zconfout = out_str ; +} + +int zconfget_debug (void) +{ + return zconf_flex_debug; +} + +void zconfset_debug (int bdebug ) +{ + zconf_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from zconflex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + zconfin = stdin; + zconfout = stdout; +#else + zconfin = (FILE *) 0; + zconfout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * zconflex_init() + */ + return 0; +} + +/* zconflex_destroy is for both reentrant and non-reentrant scanners. */ +int zconflex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + zconf_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + zconfpop_buffer_state(); + } + + /* Destroy the stack itself. */ + zconffree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * zconflex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *zconfalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *zconfrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void zconffree (void * ptr ) +{ + free( (char *) ptr ); /* see zconfrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +void zconf_starthelp(void) +{ + new_string(); + last_ts = first_ts = 0; + BEGIN(HELP); +} + +static void zconf_endhelp(void) +{ + zconflval.string = text; + BEGIN(INITIAL); +} + +/* + * Try to open specified file with following names: + * ./name + * $(srctree)/name + * The latter is used when srctree is separate from objtree + * when compiling the kernel. + * Return NULL if file is not found. + */ +FILE *zconf_fopen(const char *name) +{ + char *env, fullname[PATH_MAX+1]; + FILE *f; + + f = fopen(name, "r"); + if (!f && name != NULL && name[0] != '/') { + env = getenv(SRCTREE); + if (env) { + sprintf(fullname, "%s/%s", env, name); + f = fopen(fullname, "r"); + } + } + return f; +} + +void zconf_initscan(const char *name) +{ + zconfin = zconf_fopen(name); + if (!zconfin) { + printf("can't find file %s\n", name); + exit(1); + } + + current_buf = xmalloc(sizeof(*current_buf)); + memset(current_buf, 0, sizeof(*current_buf)); + + current_file = file_lookup(name); + current_file->lineno = 1; +} + +void zconf_nextfile(const char *name) +{ + struct file *iter; + struct file *file = file_lookup(name); + struct buffer *buf = xmalloc(sizeof(*buf)); + memset(buf, 0, sizeof(*buf)); + + current_buf->state = YY_CURRENT_BUFFER; + zconfin = zconf_fopen(file->name); + if (!zconfin) { + printf("%s:%d: can't open file \"%s\"\n", + zconf_curname(), zconf_lineno(), file->name); + exit(1); + } + zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE)); + buf->parent = current_buf; + current_buf = buf; + + for (iter = current_file->parent; iter; iter = iter->parent ) { + if (!strcmp(current_file->name,iter->name) ) { + printf("%s:%d: recursive inclusion detected. " + "Inclusion path:\n current file : '%s'\n", + zconf_curname(), zconf_lineno(), + zconf_curname()); + iter = current_file->parent; + while (iter && \ + strcmp(iter->name,current_file->name)) { + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno-1); + iter = iter->parent; + } + if (iter) + printf(" included from: '%s:%d'\n", + iter->name, iter->lineno+1); + exit(1); + } + } + file->lineno = 1; + file->parent = current_file; + current_file = file; +} + +static void zconf_endfile(void) +{ + struct buffer *parent; + + current_file = current_file->parent; + + parent = current_buf->parent; + if (parent) { + fclose(zconfin); + zconf_delete_buffer(YY_CURRENT_BUFFER); + zconf_switch_to_buffer(parent->state); + } + free(current_buf); + current_buf = parent; +} + +int zconf_lineno(void) +{ + return current_pos.lineno; +} + +const char *zconf_curname(void) +{ + return current_pos.file ? current_pos.file->name : ""; +} + diff --git a/tools/kconfig/zconf.tab.c_shipped b/tools/kconfig/zconf.tab.c_shipped new file mode 100644 index 0000000..65b7515 --- /dev/null +++ b/tools/kconfig/zconf.tab.c_shipped @@ -0,0 +1,2471 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 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 3 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 . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0.4" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse zconfparse +#define yylex zconflex +#define yyerror zconferror +#define yydebug zconfdebug +#define yynerrs zconfnerrs + +#define yylval zconflval +#define yychar zconfchar + +/* Copy the first part of user declarations. */ + + +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#include "lkc.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[SYMBOL_HASHSIZE]; + +static struct menu *current_menu, *current_entry; + + + + +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int zconfdebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + T_MAINMENU = 258, + T_MENU = 259, + T_ENDMENU = 260, + T_SOURCE = 261, + T_CHOICE = 262, + T_ENDCHOICE = 263, + T_COMMENT = 264, + T_CONFIG = 265, + T_MENUCONFIG = 266, + T_HELP = 267, + T_HELPTEXT = 268, + T_IF = 269, + T_ENDIF = 270, + T_DEPENDS = 271, + T_OPTIONAL = 272, + T_PROMPT = 273, + T_TYPE = 274, + T_DEFAULT = 275, + T_SELECT = 276, + T_IMPLY = 277, + T_RANGE = 278, + T_VISIBLE = 279, + T_OPTION = 280, + T_ON = 281, + T_WORD = 282, + T_WORD_QUOTE = 283, + T_UNEQUAL = 284, + T_LESS = 285, + T_LESS_EQUAL = 286, + T_GREATER = 287, + T_GREATER_EQUAL = 288, + T_CLOSE_PAREN = 289, + T_OPEN_PAREN = 290, + T_EOL = 291, + T_OR = 292, + T_AND = 293, + T_EQUAL = 294, + T_NOT = 295 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED + +union YYSTYPE +{ + + + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + const struct kconf_id *id; + + +}; + +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE zconflval; + +int zconfparse (void); + + + +/* Copy the second part of user declarations. */ + + +/* Include zconf.hash.c here so it can see the token constants. */ +#include "zconf.hash.c" + + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + +#if !defined _Noreturn \ + && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +# if defined _MSC_VER && 1200 <= _MSC_VER +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 11 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 301 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 41 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 50 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 124 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 204 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 295 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 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, 32, 33, 34, + 35, 36, 37, 38, 39, 40 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 109, 109, 109, 111, 111, 113, 115, 116, 117, + 118, 119, 120, 124, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 132, 133, 134, 135, 136, 137, 141, + 142, 148, 156, 162, 170, 180, 182, 183, 184, 185, + 186, 187, 190, 198, 204, 214, 220, 226, 232, 235, + 237, 248, 249, 254, 263, 268, 276, 279, 281, 282, + 283, 284, 285, 288, 294, 305, 311, 321, 323, 328, + 336, 344, 347, 349, 350, 351, 356, 363, 370, 375, + 383, 386, 388, 389, 390, 393, 401, 408, 415, 421, + 428, 430, 431, 432, 435, 443, 445, 446, 449, 456, + 458, 463, 464, 467, 468, 469, 473, 474, 477, 478, + 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, + 491, 494, 495, 498, 499 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 0 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU", + "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", + "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", + "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", "T_SELECT", "T_IMPLY", + "T_RANGE", "T_VISIBLE", "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE", + "T_UNEQUAL", "T_LESS", "T_LESS_EQUAL", "T_GREATER", "T_GREATER_EQUAL", + "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL", + "T_NOT", "$accept", "input", "start", "stmt_list", "option_name", + "common_stmt", "option_error", "config_entry_start", "config_stmt", + "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", + "config_option", "symbol_option", "symbol_option_list", + "symbol_option_arg", "choice", "choice_entry", "choice_end", + "choice_stmt", "choice_option_list", "choice_option", "choice_block", + "if_entry", "if_end", "if_stmt", "if_block", "mainmenu_stmt", "menu", + "menu_entry", "menu_end", "menu_stmt", "menu_block", "source_stmt", + "comment", "comment_stmt", "help_start", "help", "depends_list", + "depends", "visibility_list", "visible", "prompt_stmt_opt", "prompt", + "end", "nl", "if_expr", "expr", "symbol", "word_opt", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295 +}; +# endif + +#define YYPACT_NINF -92 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-92))) + +#define YYTABLE_NINF -88 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 17, 41, -92, 15, -92, 150, -92, 19, -92, -92, + -13, -92, 28, 41, 38, 41, 50, 47, 41, 79, + 82, 44, 76, -92, -92, -92, -92, -92, -92, -92, + -92, -92, 118, -92, 129, -92, -92, -92, -92, -92, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, -92, 184, -92, -92, 107, -92, 111, -92, 113, + -92, 116, -92, 139, 140, 151, -92, -92, 44, 44, + 142, 256, -92, 160, 173, 27, 117, 80, 51, 255, + -15, 255, 217, -92, -92, -92, -92, -92, -92, -8, + -92, 44, 44, 107, 87, 87, 87, 87, 87, 87, + -92, -92, 174, 176, 187, 41, 41, 44, 188, 189, + 87, -92, 213, -92, -92, -92, -92, 206, -92, -92, + 193, 41, 41, 203, -92, -92, -92, -92, -92, -92, + -92, -92, -92, -92, -92, -92, -92, 229, -92, 241, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + 216, -92, -92, -92, -92, -92, -92, -92, -92, -92, + 44, 229, 222, 229, 64, 229, 229, 87, 31, 231, + -92, -92, 229, 236, 229, 44, -92, 145, 242, -92, + -92, 243, 244, 245, 229, 251, -92, -92, 247, -92, + 257, 125, -92, -92, -92, -92, -92, 260, 41, -92, + -92, -92, -92, -92 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 6, 0, 106, 0, 3, 0, 6, 6, 101, 102, + 0, 1, 0, 0, 0, 0, 123, 0, 0, 0, + 0, 0, 0, 14, 19, 15, 16, 21, 17, 18, + 20, 22, 0, 23, 0, 7, 35, 26, 35, 27, + 57, 67, 8, 72, 24, 95, 81, 9, 28, 90, + 25, 10, 0, 107, 2, 76, 13, 0, 103, 0, + 124, 0, 104, 0, 0, 0, 121, 122, 0, 0, + 0, 110, 105, 0, 0, 0, 0, 0, 0, 0, + 90, 0, 0, 77, 85, 53, 86, 31, 33, 0, + 118, 0, 0, 69, 0, 0, 0, 0, 0, 0, + 11, 12, 0, 0, 0, 0, 99, 0, 0, 0, + 0, 49, 0, 41, 40, 36, 37, 0, 39, 38, + 0, 0, 99, 0, 61, 62, 58, 60, 59, 68, + 56, 55, 73, 75, 71, 74, 70, 108, 97, 0, + 96, 82, 84, 80, 83, 79, 92, 93, 91, 117, + 119, 120, 116, 111, 112, 113, 114, 115, 30, 88, + 0, 108, 0, 108, 108, 108, 108, 0, 0, 0, + 89, 65, 108, 0, 108, 0, 98, 0, 0, 42, + 100, 0, 0, 0, 108, 51, 48, 29, 0, 64, + 0, 109, 94, 43, 44, 45, 46, 0, 0, 50, + 63, 66, 47, 52 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -92, -92, 285, 291, -92, 32, -66, -92, -92, -92, + -92, 261, -92, -92, -92, -92, -92, -92, -92, 1, + -92, -92, -92, -92, -92, -92, -92, -92, -92, -92, + -92, 24, -92, -92, -92, -92, -92, 221, 220, -64, + -92, -92, 179, -1, 67, 0, 110, -67, -91, -92 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 3, 4, 5, 34, 35, 114, 36, 37, 38, + 39, 75, 115, 116, 168, 199, 40, 41, 130, 42, + 77, 126, 78, 43, 134, 44, 79, 6, 45, 46, + 143, 47, 81, 48, 49, 50, 117, 118, 82, 119, + 80, 140, 162, 163, 51, 7, 176, 70, 71, 61 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 10, 89, 90, 152, 153, 154, 155, 156, 157, 137, + 55, 125, 57, 128, 59, 11, 147, 63, 148, 167, + 1, 138, 1, 2, 150, 151, 149, -32, 102, 91, + 92, -32, -32, -32, -32, -32, -32, -32, -32, 103, + 164, -32, -32, 104, -32, 105, 106, 107, 108, 109, + 110, -32, 111, 2, 112, 53, 14, 15, 185, 17, + 18, 19, 20, 113, 56, 21, 22, 186, 8, 9, + 93, 66, 67, 147, 58, 148, 184, 60, 175, 68, + 133, 102, 142, 62, 69, -54, -54, 33, -54, -54, + -54, -54, 103, 177, -54, -54, 104, 120, 121, 122, + 123, 91, 92, 135, 161, 144, 64, 112, 191, 65, + 129, 132, 72, 141, 66, 67, 124, -34, 102, 73, + 172, -34, -34, -34, -34, -34, -34, -34, -34, 103, + 74, -34, -34, 104, -34, 105, 106, 107, 108, 109, + 110, -34, 111, 53, 112, 131, 136, 83, 145, 84, + -5, 12, 85, 113, 13, 14, 15, 16, 17, 18, + 19, 20, 91, 92, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 86, 87, 32, 2, 91, + 92, 192, 91, 92, -4, 12, 33, 88, 13, 14, + 15, 16, 17, 18, 19, 20, 100, 203, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 101, + 158, 32, 159, 160, 169, 165, 166, -87, 102, 170, + 33, -87, -87, -87, -87, -87, -87, -87, -87, 171, + 174, -87, -87, 104, -87, -87, -87, -87, -87, -87, + -87, -87, 102, 175, 112, -78, -78, -78, -78, -78, + -78, -78, -78, 146, 92, -78, -78, 104, 179, 13, + 14, 15, 16, 17, 18, 19, 20, 187, 112, 21, + 22, 178, 189, 180, 181, 182, 183, 146, 193, 194, + 195, 196, 188, 200, 190, 94, 95, 96, 97, 98, + 198, 33, 54, 201, 197, 99, 202, 52, 127, 76, + 139, 173 +}; + +static const yytype_uint8 yycheck[] = +{ + 1, 68, 69, 94, 95, 96, 97, 98, 99, 24, + 10, 77, 13, 77, 15, 0, 82, 18, 82, 110, + 3, 36, 3, 36, 91, 92, 34, 0, 1, 37, + 38, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 107, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 36, 27, 36, 5, 6, 27, 8, + 9, 10, 11, 36, 36, 14, 15, 36, 27, 28, + 70, 27, 28, 139, 36, 139, 167, 27, 14, 35, + 79, 1, 81, 36, 40, 5, 6, 36, 8, 9, + 10, 11, 12, 160, 14, 15, 16, 17, 18, 19, + 20, 37, 38, 79, 105, 81, 27, 27, 175, 27, + 78, 79, 36, 81, 27, 28, 36, 0, 1, 1, + 121, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 36, 27, 78, 79, 36, 81, 36, + 0, 1, 36, 36, 4, 5, 6, 7, 8, 9, + 10, 11, 37, 38, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 36, 36, 27, 36, 37, + 38, 36, 37, 38, 0, 1, 36, 36, 4, 5, + 6, 7, 8, 9, 10, 11, 36, 198, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 36, + 36, 27, 36, 26, 1, 27, 27, 0, 1, 13, + 36, 4, 5, 6, 7, 8, 9, 10, 11, 36, + 27, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 1, 14, 27, 4, 5, 6, 7, 8, + 9, 10, 11, 36, 38, 14, 15, 16, 36, 4, + 5, 6, 7, 8, 9, 10, 11, 36, 27, 14, + 15, 161, 36, 163, 164, 165, 166, 36, 36, 36, + 36, 36, 172, 36, 174, 29, 30, 31, 32, 33, + 39, 36, 7, 36, 184, 39, 36, 6, 77, 38, + 80, 122 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 3, 36, 42, 43, 44, 68, 86, 27, 28, + 84, 0, 1, 4, 5, 6, 7, 8, 9, 10, + 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 27, 36, 45, 46, 48, 49, 50, 51, + 57, 58, 60, 64, 66, 69, 70, 72, 74, 75, + 76, 85, 44, 36, 43, 86, 36, 84, 36, 84, + 27, 90, 36, 84, 27, 27, 27, 28, 35, 40, + 88, 89, 36, 1, 1, 52, 52, 61, 63, 67, + 81, 73, 79, 36, 36, 36, 36, 36, 36, 88, + 88, 37, 38, 86, 29, 30, 31, 32, 33, 39, + 36, 36, 1, 12, 16, 18, 19, 20, 21, 22, + 23, 25, 27, 36, 47, 53, 54, 77, 78, 80, + 17, 18, 19, 20, 36, 47, 62, 78, 80, 46, + 59, 85, 46, 60, 65, 72, 85, 24, 36, 79, + 82, 46, 60, 71, 72, 85, 36, 47, 80, 34, + 88, 88, 89, 89, 89, 89, 89, 89, 36, 36, + 26, 84, 83, 84, 88, 27, 27, 89, 55, 1, + 13, 36, 84, 83, 27, 14, 87, 88, 87, 36, + 87, 87, 87, 87, 89, 27, 36, 36, 87, 36, + 87, 88, 36, 36, 36, 36, 36, 87, 39, 56, + 36, 36, 36, 84 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 41, 42, 42, 43, 43, 44, 44, 44, 44, + 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, + 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, + 47, 48, 49, 50, 51, 52, 52, 52, 52, 52, + 52, 52, 53, 53, 53, 53, 53, 53, 54, 55, + 55, 56, 56, 57, 58, 59, 60, 61, 61, 61, + 61, 61, 61, 62, 62, 62, 62, 63, 63, 64, + 65, 66, 67, 67, 67, 67, 68, 69, 70, 71, + 72, 73, 73, 73, 73, 74, 75, 76, 77, 78, + 79, 79, 79, 79, 80, 81, 81, 81, 82, 83, + 83, 84, 84, 85, 85, 85, 86, 86, 87, 87, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 89, 89, 90, 90 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 2, 1, 2, 1, 0, 2, 2, 2, + 2, 4, 4, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 2, 3, 2, 3, 2, 0, 2, 2, 2, 2, + 2, 2, 3, 4, 4, 4, 4, 5, 3, 0, + 3, 0, 2, 3, 2, 1, 3, 0, 2, 2, + 2, 2, 2, 4, 3, 2, 4, 0, 2, 3, + 1, 3, 0, 2, 2, 2, 3, 3, 3, 1, + 3, 0, 2, 2, 2, 3, 3, 2, 2, 2, + 0, 2, 2, 2, 4, 0, 2, 2, 2, 0, + 2, 1, 1, 2, 2, 2, 1, 2, 0, 2, + 1, 3, 3, 3, 3, 3, 3, 3, 2, 3, + 3, 1, 1, 0, 1 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +{ + YYUSE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yytype) + { + case 58: /* choice_entry */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + ((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno); + if (current_menu == ((*yyvaluep).menu)) + menu_end_menu(); +} + + break; + + case 64: /* if_entry */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + ((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno); + if (current_menu == ((*yyvaluep).menu)) + menu_end_menu(); +} + + break; + + case 70: /* menu_entry */ + + { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + ((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno); + if (current_menu == ((*yyvaluep).menu)) + menu_end_menu(); +} + + break; + + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 10: + + { zconf_error("unexpected end statement"); } + + break; + + case 11: + + { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); } + + break; + + case 12: + + { + zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name); +} + + break; + + case 13: + + { zconf_error("invalid statement"); } + + break; + + case 29: + + { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); } + + break; + + case 30: + + { zconf_error("invalid option"); } + + break; + + case 31: + + { + struct symbol *sym = sym_lookup((yyvsp[-1].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); +} + + break; + + case 32: + + { + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 33: + + { + struct symbol *sym = sym_lookup((yyvsp[-1].string), 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); +} + + break; + + case 34: + + { + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 42: + + { + menu_set_type((yyvsp[-2].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[-2].id)->stype); +} + + break; + + case 43: + + { + menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 44: + + { + menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr)); + if ((yyvsp[-3].id)->stype != S_UNKNOWN) + menu_set_type((yyvsp[-3].id)->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[-3].id)->stype); +} + + break; + + case 45: + + { + menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 46: + + { + menu_add_symbol(P_IMPLY, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 47: + + { + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 50: + + { + const struct kconf_id *id = kconf_id_lookup((yyvsp[-1].string), strlen((yyvsp[-1].string))); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, (yyvsp[0].string)); + else + zconfprint("warning: ignoring unknown option %s", (yyvsp[-1].string)); + free((yyvsp[-1].string)); +} + + break; + + case 51: + + { (yyval.string) = NULL; } + + break; + + case 52: + + { (yyval.string) = (yyvsp[0].string); } + + break; + + case 53: + + { + struct symbol *sym = sym_lookup((yyvsp[-1].string), SYMBOL_CHOICE); + sym->flags |= SYMBOL_AUTO; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 54: + + { + (yyval.menu) = menu_add_menu(); +} + + break; + + case 55: + + { + if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +} + + break; + + case 63: + + { + menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 64: + + { + if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) { + menu_set_type((yyvsp[-2].id)->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + (yyvsp[-2].id)->stype); + } else + YYERROR; +} + + break; + + case 65: + + { + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 66: + + { + if ((yyvsp[-3].id)->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +} + + break; + + case 69: + + { + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep((yyvsp[-1].expr)); + (yyval.menu) = menu_add_menu(); +} + + break; + + case 70: + + { + if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +} + + break; + + case 76: + + { + menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL); +} + + break; + + case 77: + + { + menu_add_entry(NULL); + menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 78: + + { + (yyval.menu) = menu_add_menu(); +} + + break; + + case 79: + + { + if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +} + + break; + + case 85: + + { + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); + zconf_nextfile((yyvsp[-1].string)); +} + + break; + + case 86: + + { + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 87: + + { + menu_end_entry(); +} + + break; + + case 88: + + { + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +} + + break; + + case 89: + + { + current_entry->help = (yyvsp[0].string); +} + + break; + + case 94: + + { + menu_add_dep((yyvsp[-1].expr)); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +} + + break; + + case 98: + + { + menu_add_visibility((yyvsp[0].expr)); +} + + break; + + case 100: + + { + menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr)); +} + + break; + + case 103: + + { (yyval.id) = (yyvsp[-1].id); } + + break; + + case 104: + + { (yyval.id) = (yyvsp[-1].id); } + + break; + + case 105: + + { (yyval.id) = (yyvsp[-1].id); } + + break; + + case 108: + + { (yyval.expr) = NULL; } + + break; + + case 109: + + { (yyval.expr) = (yyvsp[0].expr); } + + break; + + case 110: + + { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); } + + break; + + case 111: + + { (yyval.expr) = expr_alloc_comp(E_LTH, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 112: + + { (yyval.expr) = expr_alloc_comp(E_LEQ, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 113: + + { (yyval.expr) = expr_alloc_comp(E_GTH, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 114: + + { (yyval.expr) = expr_alloc_comp(E_GEQ, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 115: + + { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 116: + + { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); } + + break; + + case 117: + + { (yyval.expr) = (yyvsp[-1].expr); } + + break; + + case 118: + + { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); } + + break; + + case 119: + + { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); } + + break; + + case 120: + + { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); } + + break; + + case 121: + + { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); } + + break; + + case 122: + + { (yyval.symbol) = sym_lookup((yyvsp[0].string), SYMBOL_CONST); free((yyvsp[0].string)); } + + break; + + case 123: + + { (yyval.string) = NULL; } + + break; + + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} + + + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + _menu_init(); + rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); + + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym) + modules_sym = sym_find( "n" ); + + rootmenu.prompt->text = _(rootmenu.prompt->text); + rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +static const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + case T_VISIBLE: return "visible"; + } + return ""; +} + +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_IMPLY: + fputs( " imply ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "zconf.lex.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" diff --git a/tools/kconfig/zconf.y b/tools/kconfig/zconf.y new file mode 100644 index 0000000..001305f --- /dev/null +++ b/tools/kconfig/zconf.y @@ -0,0 +1,754 @@ +%{ +/* + * Copyright (C) 2002 Roman Zippel + * Released under the terms of the GNU GPL v2.0. + */ + +#include +#include +#include +#include +#include +#include + +#include "lkc.h" + +#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) + +#define PRINTD 0x0001 +#define DEBUG_PARSE 0x0002 + +int cdebug = PRINTD; + +extern int zconflex(void); +static void zconfprint(const char *err, ...); +static void zconf_error(const char *err, ...); +static void zconferror(const char *err); +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); + +struct symbol *symbol_hash[SYMBOL_HASHSIZE]; + +static struct menu *current_menu, *current_entry; + +%} +%expect 32 + +%union +{ + char *string; + struct file *file; + struct symbol *symbol; + struct expr *expr; + struct menu *menu; + const struct kconf_id *id; +} + +%token T_MAINMENU +%token T_MENU +%token T_ENDMENU +%token T_SOURCE +%token T_CHOICE +%token T_ENDCHOICE +%token T_COMMENT +%token T_CONFIG +%token T_MENUCONFIG +%token T_HELP +%token T_HELPTEXT +%token T_IF +%token T_ENDIF +%token T_DEPENDS +%token T_OPTIONAL +%token T_PROMPT +%token T_TYPE +%token T_DEFAULT +%token T_SELECT +%token T_IMPLY +%token T_RANGE +%token T_VISIBLE +%token T_OPTION +%token T_ON +%token T_WORD +%token T_WORD_QUOTE +%token T_UNEQUAL +%token T_LESS +%token T_LESS_EQUAL +%token T_GREATER +%token T_GREATER_EQUAL +%token T_CLOSE_PAREN +%token T_OPEN_PAREN +%token T_EOL + +%left T_OR +%left T_AND +%left T_EQUAL T_UNEQUAL +%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL +%nonassoc T_NOT + +%type prompt +%type symbol +%type expr +%type if_expr +%type end +%type option_name +%type if_entry menu_entry choice_entry +%type symbol_option_arg word_opt + +%destructor { + fprintf(stderr, "%s:%d: missing end statement for this entry\n", + $$->file->name, $$->lineno); + if (current_menu == $$) + menu_end_menu(); +} if_entry menu_entry choice_entry + +%{ +/* Include zconf.hash.c here so it can see the token constants. */ +#include "zconf.hash.c" +%} + +%% +input: nl start | start; + +start: mainmenu_stmt stmt_list | stmt_list; + +stmt_list: + /* empty */ + | stmt_list common_stmt + | stmt_list choice_stmt + | stmt_list menu_stmt + | stmt_list end { zconf_error("unexpected end statement"); } + | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } + | stmt_list option_name error T_EOL +{ + zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name); +} + | stmt_list error T_EOL { zconf_error("invalid statement"); } +; + +option_name: + T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_IMPLY | T_OPTIONAL | T_RANGE | T_DEFAULT | T_VISIBLE +; + +common_stmt: + T_EOL + | if_stmt + | comment_stmt + | config_stmt + | menuconfig_stmt + | source_stmt +; + +option_error: + T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); } + | error T_EOL { zconf_error("invalid option"); } +; + + +/* config/menuconfig entry */ + +config_entry_start: T_CONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +config_stmt: config_entry_start config_option_list +{ + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL +{ + struct symbol *sym = sym_lookup($2, 0); + sym->flags |= SYMBOL_OPTIONAL; + menu_add_entry(sym); + printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2); +}; + +menuconfig_stmt: menuconfig_entry_start config_option_list +{ + if (current_entry->prompt) + current_entry->prompt->type = P_MENU; + else + zconfprint("warning: menuconfig statement without prompt"); + menu_end_entry(); + printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); +}; + +config_option_list: + /* empty */ + | config_option_list config_option + | config_option_list symbol_option + | config_option_list depends + | config_option_list help + | config_option_list option_error + | config_option_list T_EOL +; + +config_option: T_TYPE prompt_stmt_opt T_EOL +{ + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_DEFAULT expr if_expr T_EOL +{ + menu_add_expr(P_DEFAULT, $2, $3); + if ($1->stype != S_UNKNOWN) + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:default(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); +}; + +config_option: T_SELECT T_WORD if_expr T_EOL +{ + menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_IMPLY T_WORD if_expr T_EOL +{ + menu_add_symbol(P_IMPLY, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno()); +}; + +config_option: T_RANGE symbol symbol if_expr T_EOL +{ + menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); + printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); +}; + +symbol_option: T_OPTION symbol_option_list T_EOL +; + +symbol_option_list: + /* empty */ + | symbol_option_list T_WORD symbol_option_arg +{ + const struct kconf_id *id = kconf_id_lookup($2, strlen($2)); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, $3); + else + zconfprint("warning: ignoring unknown option %s", $2); + free($2); +}; + +symbol_option_arg: + /* empty */ { $$ = NULL; } + | T_EQUAL prompt { $$ = $2; } +; + +/* choice entry */ + +choice: T_CHOICE word_opt T_EOL +{ + struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE); + sym->flags |= SYMBOL_AUTO; + menu_add_entry(sym); + menu_add_expr(P_CHOICE, NULL, NULL); + printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); +}; + +choice_entry: choice choice_option_list +{ + $$ = menu_add_menu(); +}; + +choice_end: end +{ + if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); + } +}; + +choice_stmt: choice_entry choice_block choice_end +; + +choice_option_list: + /* empty */ + | choice_option_list choice_option + | choice_option_list depends + | choice_option_list help + | choice_option_list T_EOL + | choice_option_list option_error +; + +choice_option: T_PROMPT prompt if_expr T_EOL +{ + menu_add_prompt(P_PROMPT, $2, $3); + printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_TYPE prompt_stmt_opt T_EOL +{ + if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { + menu_set_type($1->stype); + printd(DEBUG_PARSE, "%s:%d:type(%u)\n", + zconf_curname(), zconf_lineno(), + $1->stype); + } else + YYERROR; +}; + +choice_option: T_OPTIONAL T_EOL +{ + current_entry->sym->flags |= SYMBOL_OPTIONAL; + printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); +}; + +choice_option: T_DEFAULT T_WORD if_expr T_EOL +{ + if ($1->stype == S_UNKNOWN) { + menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3); + printd(DEBUG_PARSE, "%s:%d:default\n", + zconf_curname(), zconf_lineno()); + } else + YYERROR; +}; + +choice_block: + /* empty */ + | choice_block common_stmt +; + +/* if entry */ + +if_entry: T_IF expr nl +{ + printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); + menu_add_entry(NULL); + menu_add_dep($2); + $$ = menu_add_menu(); +}; + +if_end: end +{ + if (zconf_endtoken($1, T_IF, T_ENDIF)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); + } +}; + +if_stmt: if_entry if_block if_end +; + +if_block: + /* empty */ + | if_block common_stmt + | if_block menu_stmt + | if_block choice_stmt +; + +/* mainmenu entry */ + +mainmenu_stmt: T_MAINMENU prompt nl +{ + menu_add_prompt(P_MENU, $2, NULL); +}; + +/* menu entry */ + +menu: T_MENU prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_MENU, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); +}; + +menu_entry: menu visibility_list depends_list +{ + $$ = menu_add_menu(); +}; + +menu_end: end +{ + if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { + menu_end_menu(); + printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); + } +}; + +menu_stmt: menu_entry menu_block menu_end +; + +menu_block: + /* empty */ + | menu_block common_stmt + | menu_block menu_stmt + | menu_block choice_stmt +; + +source_stmt: T_SOURCE prompt T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); + zconf_nextfile($2); +}; + +/* comment entry */ + +comment: T_COMMENT prompt T_EOL +{ + menu_add_entry(NULL); + menu_add_prompt(P_COMMENT, $2, NULL); + printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); +}; + +comment_stmt: comment depends_list +{ + menu_end_entry(); +}; + +/* help option */ + +help_start: T_HELP T_EOL +{ + printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); + zconf_starthelp(); +}; + +help: help_start T_HELPTEXT +{ + current_entry->help = $2; +}; + +/* depends option */ + +depends_list: + /* empty */ + | depends_list depends + | depends_list T_EOL + | depends_list option_error +; + +depends: T_DEPENDS T_ON expr T_EOL +{ + menu_add_dep($3); + printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); +}; + +/* visibility option */ + +visibility_list: + /* empty */ + | visibility_list visible + | visibility_list T_EOL +; + +visible: T_VISIBLE if_expr +{ + menu_add_visibility($2); +}; + +/* prompt statement */ + +prompt_stmt_opt: + /* empty */ + | prompt if_expr +{ + menu_add_prompt(P_PROMPT, $1, $2); +}; + +prompt: T_WORD + | T_WORD_QUOTE +; + +end: T_ENDMENU T_EOL { $$ = $1; } + | T_ENDCHOICE T_EOL { $$ = $1; } + | T_ENDIF T_EOL { $$ = $1; } +; + +nl: + T_EOL + | nl T_EOL +; + +if_expr: /* empty */ { $$ = NULL; } + | T_IF expr { $$ = $2; } +; + +expr: symbol { $$ = expr_alloc_symbol($1); } + | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } + | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } + | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } + | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } + | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } + | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } + | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } + | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } + | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } + | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } +; + +symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); } + | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } +; + +word_opt: /* empty */ { $$ = NULL; } + | T_WORD + +%% + +void conf_parse(const char *name) +{ + struct symbol *sym; + int i; + + zconf_initscan(name); + + sym_init(); + _menu_init(); + rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); + + if (getenv("ZCONF_DEBUG")) + zconfdebug = 1; + zconfparse(); + if (zconfnerrs) + exit(1); + if (!modules_sym) + modules_sym = sym_find( "n" ); + + rootmenu.prompt->text = _(rootmenu.prompt->text); + rootmenu.prompt->text = sym_expand_string_value(rootmenu.prompt->text); + + menu_finalize(&rootmenu); + for_all_symbols(i, sym) { + if (sym_check_deps(sym)) + zconfnerrs++; + } + if (zconfnerrs) + exit(1); + sym_set_change_count(1); +} + +static const char *zconf_tokenname(int token) +{ + switch (token) { + case T_MENU: return "menu"; + case T_ENDMENU: return "endmenu"; + case T_CHOICE: return "choice"; + case T_ENDCHOICE: return "endchoice"; + case T_IF: return "if"; + case T_ENDIF: return "endif"; + case T_DEPENDS: return "depends"; + case T_VISIBLE: return "visible"; + } + return ""; +} + +static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) +{ + if (id->token != endtoken) { + zconf_error("unexpected '%s' within %s block", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + if (current_menu->file != current_file) { + zconf_error("'%s' in different file than '%s'", + kconf_id_strings + id->name, zconf_tokenname(starttoken)); + fprintf(stderr, "%s:%d: location of the '%s'\n", + current_menu->file->name, current_menu->lineno, + zconf_tokenname(starttoken)); + zconfnerrs++; + return false; + } + return true; +} + +static void zconfprint(const char *err, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconf_error(const char *err, ...) +{ + va_list ap; + + zconfnerrs++; + fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); + va_start(ap, err); + vfprintf(stderr, err, ap); + va_end(ap); + fprintf(stderr, "\n"); +} + +static void zconferror(const char *err) +{ + fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); +} + +static void print_quoted_string(FILE *out, const char *str) +{ + const char *p; + int len; + + putc('"', out); + while ((p = strchr(str, '"'))) { + len = p - str; + if (len) + fprintf(out, "%.*s", len, str); + fputs("\\\"", out); + str = p + 1; + } + fputs(str, out); + putc('"', out); +} + +static void print_symbol(FILE *out, struct menu *menu) +{ + struct symbol *sym = menu->sym; + struct property *prop; + + if (sym_is_choice(sym)) + fprintf(out, "\nchoice\n"); + else + fprintf(out, "\nconfig %s\n", sym->name); + switch (sym->type) { + case S_BOOLEAN: + fputs(" boolean\n", out); + break; + case S_TRISTATE: + fputs(" tristate\n", out); + break; + case S_STRING: + fputs(" string\n", out); + break; + case S_INT: + fputs(" integer\n", out); + break; + case S_HEX: + fputs(" hex\n", out); + break; + default: + fputs(" ???\n", out); + break; + } + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu != menu) + continue; + switch (prop->type) { + case P_PROMPT: + fputs(" prompt ", out); + print_quoted_string(out, prop->text); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_DEFAULT: + fputs( " default ", out); + expr_fprint(prop->expr, out); + if (!expr_is_yes(prop->visible.expr)) { + fputs(" if ", out); + expr_fprint(prop->visible.expr, out); + } + fputc('\n', out); + break; + case P_CHOICE: + fputs(" #choice value\n", out); + break; + case P_SELECT: + fputs( " select ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_IMPLY: + fputs( " imply ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_RANGE: + fputs( " range ", out); + expr_fprint(prop->expr, out); + fputc('\n', out); + break; + case P_MENU: + fputs( " menu ", out); + print_quoted_string(out, prop->text); + fputc('\n', out); + break; + default: + fprintf(out, " unknown prop %d!\n", prop->type); + break; + } + } + if (menu->help) { + int len = strlen(menu->help); + while (menu->help[--len] == '\n') + menu->help[len] = 0; + fprintf(out, " help\n%s\n", menu->help); + } +} + +void zconfdump(FILE *out) +{ + struct property *prop; + struct symbol *sym; + struct menu *menu; + + menu = rootmenu.list; + while (menu) { + if ((sym = menu->sym)) + print_symbol(out, menu); + else if ((prop = menu->prompt)) { + switch (prop->type) { + case P_COMMENT: + fputs("\ncomment ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + case P_MENU: + fputs("\nmenu ", out); + print_quoted_string(out, prop->text); + fputs("\n", out); + break; + default: + ; + } + if (!expr_is_yes(prop->visible.expr)) { + fputs(" depends ", out); + expr_fprint(prop->visible.expr, out); + fputc('\n', out); + } + } + + if (menu->list) + menu = menu->list; + else if (menu->next) + menu = menu->next; + else while ((menu = menu->parent)) { + if (menu->prompt && menu->prompt->type == P_MENU) + fputs("\nendmenu\n", out); + if (menu->next) { + menu = menu->next; + break; + } + } + } +} + +#include "zconf.lex.c" +#include "util.c" +#include "confdata.c" +#include "expr.c" +#include "symbol.c" +#include "menu.c" -- cgit v1.2.3 From 1ff3666dc29c0eacf911c57d3e6b6a62bdc9cb78 Mon Sep 17 00:00:00 2001 From: Richard Braun Date: Thu, 21 Sep 2017 01:23:37 +0200 Subject: New build system The new build system, called xbuild, is a minimalistic kbuild-like make-based build system, also using kconfig for scalable configurations. --- .gitignore | 6 +- Kconfig | 51 ++++++ Makefile | 333 +++++++++++++++++++++++++++++++++++++++ arch/x86/Kconfig | 32 ++++ arch/x86/Makefile | 54 +++++++ arch/x86/configs/amd64_defconfig | 0 arch/x86/configs/i386_defconfig | 1 + arch/x86/machine/boot.c | 6 +- arch/x86/machine/pmap.c | 46 +++--- arch/x86/machine/pmap.h | 6 +- arch/x86/machine/pmem.h | 6 +- arch/x86/machine/types.h | 6 +- doc/Makefile | 73 +++++++++ kern/Kconfig | 83 ++++++++++ kern/Makefile | 42 +++++ kern/clock.h | 2 +- kern/cpumap.h | 28 ++-- kern/kernel.c | 4 +- kern/kernel.h | 4 +- kern/kmem.c | 4 +- kern/kmem_i.h | 2 +- kern/log.c | 4 +- kern/mutex.h | 6 +- kern/mutex_types.h | 6 +- kern/percpu.c | 4 +- kern/percpu.h | 4 +- kern/shell.h | 6 +- kern/shutdown.c | 4 +- kern/spinlock.c | 2 +- kern/syscnt.c | 4 +- kern/task.c | 4 +- kern/thread.c | 22 +-- kern/thread.h | 3 +- kern/work.c | 2 +- kern/xcall.c | 2 +- test/Kconfig | 41 +++++ test/Makefile | 9 ++ test/test_mutex.c | 6 +- tools/kconfig/Makefile | 269 +++++++++++++++---------------- tools/kconfig/gconf.c | 2 +- vm/Makefile | 5 + vm/vm_map.c | 4 +- vm/vm_page.c | 6 +- 43 files changed, 959 insertions(+), 245 deletions(-) create mode 100644 Kconfig create mode 100644 Makefile create mode 100644 arch/x86/Kconfig create mode 100644 arch/x86/Makefile create mode 100644 arch/x86/configs/amd64_defconfig create mode 100644 arch/x86/configs/i386_defconfig create mode 100644 doc/Makefile create mode 100644 kern/Kconfig create mode 100644 kern/Makefile create mode 100644 test/Kconfig create mode 100644 test/Makefile create mode 100644 vm/Makefile diff --git a/.gitignore b/.gitignore index 61d2f51..2dfdd96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ +.* *.o -cscope.out +*.d tags +cscope.out x15.lds +/include/generated /x15 -/x15.sorted_init_ops /doc/*\.[1-9] /doc/*\.[1-9]\.html diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..cf42e9f --- /dev/null +++ b/Kconfig @@ -0,0 +1,51 @@ +# +# For a description of the syntax of this configuration file, +# see doc/kbuild/kconfig-language.txt. +# +mainmenu "X15/$ARCH $VERSION Kernel Configuration" + +config ARCH + string + option env="ARCH" + +config VERSION + string + option env="VERSION" + +config KERNEL_VERSION + string + default VERSION + +config CC + string + option env="CC" + +config CFLAGS + string + option env="CFLAGS" + +menu "Build options" + +config CC_EXE + string "Compiler executable" + default CC + ---help--- + Name of the compiler executable + +config CC_OPTIONS + string "Compilation options" + default CFLAGS + ---help--- + Raw options passed to the compiler. + +config ASSERT + bool "Assertions" + default y + ---help--- + Enable assert() code generation. + +endmenu + +source "arch/$ARCH/Kconfig" +source "kern/Kconfig" +source "test/Kconfig" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..33e8c14 --- /dev/null +++ b/Makefile @@ -0,0 +1,333 @@ +SHELL := /bin/sh + +MAKEFLAGS += -rR +MAKEFLAGS += --no-print-directory + +.PHONY: all +all: x15 docs + +VERSION = 0.1 +export VERSION + +ifndef V +V := 0 +endif + +# Use callable variables so that commands can be split into multiple +# lines, but produce a single line when echoed. This makes copying +# commands easy. It also makes them somewhat self-describing. + +ifeq ($(V),0) +Q := @ + +# $(call xbuild_action_print,,) +define xbuild_action_print +@printf " %-7s %s\n" $(1) $(2) +@ +endef +else ifneq ($(V),1) +$(error invalid value for V) +endif + +export Q + +# $(call xbuild_action_mkdir,) +define xbuild_action_mkdir + $(Q)mkdir -p $(dir $(1)) +endef + +# $(call xbuild_action,,) +define xbuild_action + $(call xbuild_action_mkdir,$(2)) + $(call xbuild_action_print,$(1),$(2)) +endef + +define xbuild_kconfig_invoke + $(Q)$(MAKE) -f $(SRCDIR)/$(KCONFIG_PATH)/Makefile $@ +endef + +define xbuild_gen_autoconf_h + $(call xbuild_action,GEN,$@)cat $< \ + | sed -e 's/^\([^#]\)/#define CONFIG_\1/g' \ + -e 's/=/ /' \ + | grep '^#define' > $@ +endef + +define xbuild_gen_autoconf_mk + $(call xbuild_action,GEN,$@)cat $< \ + | sed -e 's/^\([^#]\)/CONFIG_\1/g' > $@ +endef + +# $(call xbuild_check_cc_option,