From 89680305512edd1efac9595d39807f4ba4ffd708 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Sun, 6 Jun 2010 23:03:25 +0200 Subject: [PATCH] working fat system on sd card --- Makefile | 2 +- boot-programs/crt0.ccp | 147 ----- devices.hhp | 24 +- init.config | 37 +- invoke.ccp | 7 +- iris.hhp | 10 +- kernel.hhp | 2 +- mbr+fat.txt | 88 +++ mips/arch.ccp | 9 + mips/init.ccp | 11 +- mips/interrupts.ccp | 7 + mips/nanonote/Makefile.arch | 29 +- mips/nanonote/jz4740.hhp | 16 +- mips/nanonote/server/usb-server.ccp | 2 +- mips/nanonote/threadlist-sd.S | 49 ++ .../{threadlist.S => threadlist-udc.S} | 0 screen.png | Bin 0 -> 45979 bytes {boot-programs => source}/bootinit.ccp | 2 +- source/crt0.ccp | 3 +- source/elfrun.ccp | 2 +- source/fat.ccp | 550 ++++++++++++++++++ source/init.ccp | 39 +- source/partition.ccp | 129 ++++ source/sd+mmc.ccp | 383 ++++++++++-- source/test.ccp | 60 ++ {boot-programs => source}/udc.ccp | 7 +- 26 files changed, 1343 insertions(+), 272 deletions(-) delete mode 100644 boot-programs/crt0.ccp create mode 100644 mbr+fat.txt create mode 100644 mips/nanonote/threadlist-sd.S rename mips/nanonote/{threadlist.S => threadlist-udc.S} (100%) create mode 100644 screen.png rename {boot-programs => source}/bootinit.ccp (99%) create mode 100644 source/fat.ccp create mode 100644 source/partition.ccp create mode 100644 source/test.ccp rename {boot-programs => source}/udc.ccp (98%) diff --git a/Makefile b/Makefile index 30c167b..feea48a 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CC = $(CROSS)gcc LD = $(CROSS)ld OBJCOPY = $(CROSS)objcopy -headers = kernel.hh iris.hh devices.hh ui.hh $(arch_headers) +headers = kernel.hh iris.hh devices.hh ui.hh keys.hh $(arch_headers) iris_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_iris_sources) BUILT_SOURCES = $(iris_sources) $(boot_sources) diff --git a/boot-programs/crt0.ccp b/boot-programs/crt0.ccp deleted file mode 100644 index 0befa90..0000000 --- a/boot-programs/crt0.ccp +++ /dev/null @@ -1,147 +0,0 @@ -#pypp 0 -// Iris: micro-kernel for a capability-based operating system. -// boot-programs/init.S: Startup code for initial Threads. -// Copyright 2009 Bas Wijnen -// -// 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 "iris.hh" -#include "devices.hh" - -// For some unknown reason, gcc needs this to be defined. -unsigned __gxx_personality_v0 - -struct list: - list *prev, *next - -static unsigned __slots, __caps -static list *__slot_admin, *__cap_admin -static list *__first_free_slot, *__first_free_cap - -namespace Iris: - Caps my_caps - Receiver my_receiver - Thread my_thread - Memory my_memory - Cap my_call - Parent my_parent - __recv_data_t recv - - void print_caps (): - // Assume __caps to be 32. - bool used[32] - for unsigned i = 0; i < 32; ++i: - used[i] = true - unsigned num = 0 - for list *i = __first_free_cap; i; i = i->next: - used[i - __cap_admin] = false - ++num - kdebug_num (num, 2) - kdebug (":") - for unsigned i = 0; i < 32; ++i: - kdebug_char (used[i] ? '#' : '.') - kdebug_char ('\n') - - void free_slot (unsigned slot): - //kdebug ("free slot\n") - __slot_admin[slot].prev = NULL - __slot_admin[slot].next = __first_free_slot - if __slot_admin[slot].next: - __slot_admin[slot].next->prev = &__slot_admin[slot] - __first_free_slot = &__slot_admin[slot] - - void free_cap (Cap cap): - //kdebug ("free cap\n") - if cap.slot () != 0: - kdebug ("trying to free capability from non-0 slot\n") - return - list *l = &__cap_admin[cap.idx ()] - l->prev = NULL - l->next = __first_free_cap - if l->next: - l->next->prev = l - __first_free_cap = l - - unsigned alloc_slot (): - //kdebug ("alloc slot\n") - if !__first_free_slot: - // Out of slots... Probably best to raise an exception. For now, just return NO_SLOT. - kdebug ("out of slots!\n") - Iris::panic (0) - return ~0 - list *ret = __first_free_slot - __first_free_slot = ret->next - if ret->next: - ret->next->prev = NULL - return ret - __slot_admin - - Cap alloc_cap (): - //kdebug ("alloc cap\n") - if !__first_free_cap: - // Out of caps... Probably best to raise an exception. For now, just return CAP_NONE - kdebug ("out of capabilities!\n") - Iris::panic (0) - return Cap (0, CAP_NONE) - list *ret = __first_free_cap - __first_free_cap = ret->next - if ret->next: - ret->next->prev = NULL - return Cap (0, ret - __cap_admin) - -extern "C": - void run__main (unsigned slots, unsigned caps, list *slot_admin, list *cap_admin): - __slots = slots - __caps = caps - __slot_admin = slot_admin - __cap_admin = cap_admin - __first_free_slot = NULL - for unsigned i = 1; i < __slots; ++i: - Iris::free_slot (i) - __first_free_cap = NULL - for unsigned i = 6; i < __caps; ++i: - Iris::free_cap (Iris::Cap (0, i)) - Iris::my_caps = Iris::Cap (0, __caps_num) - Iris::my_receiver = Iris::Cap (0, __receiver_num) - Iris::my_thread = Iris::Cap (0, __thread_num) - Iris::my_memory = Iris::Cap (0, __memory_num) - Iris::my_call = Iris::Cap (0, __call_num) - Iris::my_parent = Iris::Cap (0, __parent_num) - Iris::recv.reply = Iris::alloc_cap () - Iris::recv.arg = Iris::alloc_cap () - Iris::Num ret = start () - Iris::my_parent.exit (ret) - Iris::my_memory.destroy (Iris::my_thread) - // The program no longer exists. If it somehow does, generate an address fault. - while true: - *(volatile unsigned *)~0 - -__asm__ volatile ("\t.globl __start\n" - "\t.set noreorder\n" - "__start:\n" - "\tbal 1f\n" - "__hack_label:\n" - "\tnop\n" - "\t.word _gp\n" - "1:\n" - "\tlw $gp, 0($ra)\n" - "\tsll $v0, $a0, 3\n" - "\tsll $v1, $a1, 3\n" - "\tsubu $sp, $sp, $v0\n" - "\tmove $a2, $sp\n" - "\tsubu $sp, $sp, $v1\n" - "\tmove $a3, $sp\n" - "\tla $t9, run__main\n" - "\tjr $t9\n" - "\tnop\n" - "\t.set reorder") diff --git a/devices.hhp b/devices.hhp index b547d33..c2ee586 100644 --- a/devices.hhp +++ b/devices.hhp @@ -39,12 +39,13 @@ namespace Iris: enum request: GET_SIZE = 0x2001 GET_CHARS - GET_PAGE + GET_ALIGN_BITS + GET_BLOCK ID /// Get the size of the string. Num get_size (): return call (CAP_MASTER_DIRECT | GET_SIZE) - /// Get exactly 16 characters. The index must be word-aligned. + /// Get exactly 16 characters. The index must be aligned to 16 bytes or align_bits, whichever is smaller. char *get_chars (Num idx, char buffer[16]): call (CAP_MASTER_DIRECT | GET_CHARS, idx) unsigned *b = (unsigned *)buffer @@ -53,14 +54,17 @@ namespace Iris: b[2] = recv.data[1].l b[3] = recv.data[1].h return buffer - /// Helper function for get_page. + /// Get the number of bits that page accesses must be aligned to. Cannot be more than PAGE_BITS. + unsigned get_align_bits (): + return call (CAP_MASTER_DIRECT | GET_ALIGN_BITS).l + /// Helper function for get_block. static Page _create_paying_page (): Page ret = my_memory.create_page () ret.set_flags (Page::PAYING, Page::PAYING) return ret - /// Get a page from the string. This need not be implemented for strings smaller than PAGE_SIZE. The index must be page-aligned. - Cap get_page (Num idx, Page ret = _create_paying_page ()): - ocall (ret, CAP_MASTER_DIRECT | GET_PAGE, idx) + /// Get a block from the string; place it at offset on page. This need not be implemented for strings smaller than PAGE_SIZE. All arguments must be aligned. + Cap get_block (Num idx, unsigned size = PAGE_SIZE, unsigned offset = 0, Page ret = _create_paying_page ()): + ocall (ret, Iris::Num (CAP_MASTER_DIRECT | GET_BLOCK, size << 16 | offset), idx) return ret /// A writable String. @@ -69,7 +73,7 @@ namespace Iris: enum request: TRUNCATE = String::ID SET_CHARS - SET_PAGE + SET_BLOCK ID /// Set the size of the string. Strings may have a limit to this setting. void truncate (Num size): @@ -77,9 +81,9 @@ namespace Iris: /// Set exactly 4 characters. The index must be word-aligned. void set_chars (Num idx, char buffer[4]): call (Num (CAP_MASTER_DIRECT | SET_CHARS, *(unsigned *)buffer), idx) - /// Overwrite a page from the string. This need not be implemented for strings smaller than PAGE_SIZE. The index must be page-aligned. The caller may lose the frame in the transaction. - void set_page (Num idx, Page page): - ocall (page, CAP_MASTER_DIRECT | SET_PAGE, idx) + /// Overwrite a block from the string with data at offset on the page. This need not be implemented for strings smaller than PAGE_SIZE. The all arguments must be aligned. The caller may lose the frame in the transaction. Only the specified part of the page is used for overwriting data. + void set_block (Num idx, Page page, unsigned size = PAGE_SIZE, unsigned offset = 0): + ocall (page, Iris::Num (CAP_MASTER_DIRECT | SET_BLOCK, size << 16 | offset), idx) // Every process which wants to be switchable through a terminal must implement this interface. struct Device : public Cap: diff --git a/init.config b/init.config index 75bce62..8ed1fff 100644 --- a/init.config +++ b/init.config @@ -1,33 +1,44 @@ # driver = '' load a file into memory to be run priviledged. # program = '' load a file into memory to be run normally. - driver driver_lcd = "lcd.elf" - driver driver_buzzer = "buzzer.elf" + #driver driver_lcd = "lcd.elf" + #driver driver_buzzer = "buzzer.elf" driver driver_gpio = "gpio.elf" - program alarm = "alarm.elf" - program gui = "gui.elf" + #program alarm = "alarm.elf" + #program gui = "gui.elf" #driver nand = "nand.elf" driver sdmmc = "sd+mmc.elf" + program partition = "partition.elf" + program fat = "fat.elf" + program test = "test.elf" # receive / [, ] = prepare to accept a capability from a named program. - receive driver_lcd / Display = display - receive driver_lcd / Setting = display_bright - receive driver_buzzer / Buzzer = buzzer + #receive driver_lcd / Display = display + #receive driver_lcd / Setting = display_bright + #receive driver_buzzer / Buzzer = buzzer receive driver_gpio / Keyboard , 0 = keyboard receive driver_gpio / Keyboard , 1 = sysreq receive driver_gpio / Event = sdmmc_gpio - receive alarm / UI = ui + #receive alarm / UI = ui receive sdmmc / WString = sdmmc + receive partition / WString, 0 = p0 + receive partition / WString, 1 = p1 + receive partition / WString, 2 = p2 + receive partition / WString, 3 = p3 + receive fat / Directory = root # sysreq use a capability as the system request keyboard. sysreq sysreq # give / [, ] = give this capability to this program when it requests it. - give gui / UI = ui - give gui / Display = display - give gui / Setting = display_bright - give gui / Buzzer = buzzer - give gui / Keyboard = keyboard + #give gui / UI = ui + #give gui / Display = display + #give gui / Setting = display_bright + #give gui / Buzzer = buzzer + #give gui / Keyboard = keyboard give sdmmc / Event = sdmmc_gpio + give partition / WString = sdmmc + give fat / WString = p0 + give test / Directory = root # include include a file as another config file. diff --git a/invoke.ccp b/invoke.ccp index ea5556b..1823c23 100644 --- a/invoke.ccp +++ b/invoke.ccp @@ -233,6 +233,9 @@ static void receiver_invoke (unsigned cmd, unsigned target, Iris::Num protected_ return case Iris::Receiver::SET_REPLY_PROTECTED_DATA & REQUEST_MASK: receiver->reply_protected_data = c->data[1] + // Adjust target protected data, so the reply will reach the caller. + if receiver == reply_target: + reply_protected = receiver->reply_protected_data break case Iris::Receiver::GET_ALARM & REQUEST_MASK: reply_num (receiver->alarm_count) @@ -701,6 +704,9 @@ static void page_invoke (unsigned cmd, unsigned target, Iris::Num protected_data t->flags |= Iris::Page::FRAME kPage_arch_update_mapping (t) break + case Iris::Page::GET_FLAGS & REQUEST_MASK: + reply_num (page->flags) + return case Iris::Page::SET_FLAGS & REQUEST_MASK: if cmd & Iris::Page::READONLY: dpanic (0, "setting page flags denied") @@ -989,7 +995,6 @@ static void kernel_invoke (unsigned target, Iris::Num protected_data, kCapabilit c->reply.reset () reply_target = (kReceiver *)protected_data.l reply_protected = reply_target->reply_protected_data - kReceiver *r = reply_target kernel_invoke ((unsigned)call_target->target, call_target->protected_data, c) return if must_wait: diff --git a/iris.hhp b/iris.hhp index 8f07de8..1d4b4ef 100644 --- a/iris.hhp +++ b/iris.hhp @@ -294,7 +294,7 @@ namespace Iris: return call (CAP_MASTER_DIRECT | ADD_ALARM, data).l void set_alarm (unsigned data): call (CAP_MASTER_DIRECT | SET_ALARM, data) - static inline void sleep (unsigned value) + inline void sleep (unsigned value) Cap create_call_capability (): icall (CAP_MASTER_DIRECT | CREATE_CALL_CAPABILITY) return get_arg () @@ -567,6 +567,10 @@ namespace Iris: my_thread.ocall (my_receiver, CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num) inline void unregister_interrupt (unsigned num): my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num) + inline void wait_for_interrupt (unsigned num): + my_receiver.set_reply_protected_data (num) + Cap ().call () + my_receiver.set_reply_protected_data (~0) inline Cap get_top_memory (): my_thread.icall (CAP_MASTER_DIRECT | Thread::PRIV_GET_TOP_MEMORY) return get_arg () @@ -576,8 +580,10 @@ namespace Iris: my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_REBOOT) void Receiver::sleep (unsigned value): - my_receiver.set_alarm (value) + set_alarm (value) Cap ().call () + inline void sleep (unsigned value): + my_receiver.sleep (value) // The start function has this prototype (there is no main function). Iris::Num start () diff --git a/kernel.hhp b/kernel.hhp index 0c7d039..d53fc10 100644 --- a/kernel.hhp +++ b/kernel.hhp @@ -122,7 +122,7 @@ struct kThread : public kObject: unsigned flags kThreadP schedule_prev, schedule_next unsigned recv_reply, recv_arg - // caps is an array of slots pointers to kCapses. + // slot[] is an array of slots pointers to kCapses. unsigned slots #ifndef NDEBUG unsigned id diff --git a/mbr+fat.txt b/mbr+fat.txt new file mode 100644 index 0000000..54008d0 --- /dev/null +++ b/mbr+fat.txt @@ -0,0 +1,88 @@ +mbr structure. Size: 200. + +000 1b8 code or fat: + 00 3 jump instruction + 03 8 OEM name + 0b 2 bytes per sector + 0d 1 sectors per cluster + 0e 2 reserved sectors (incl. boot sector, from boot sector) + 10 1 number of fats + 11 2 non fat32: number of entries in root directory + 13 2 total number of sectors on disk if < 32MB + 15 1 media descriptor (bit 2 set means removable) + 16 2 non fat32: sectors per fat + 18 2 sectors per track + 1a 2 heads + 1c 4 number of sectors before boot sector + 20 4 number of sectors on disk, if > 32MB + 24 xx 4 sectors per fat + 28 xx 2 bit 0-4: active fat, bit 7 set: write to all fats. + 2a xx 2 file system version number + 2c xx 4 cluster number for root directory + 30 xx 2 sector number for FSInfo structure + 32 xx 2 boot sector backup sector + 34 xx c reserved + 40 24 1 drive number + 41 25 1 current head (dos internal) + 42 26 1 boot signature (with 0x29, the next 3 fields are valid) + 43 27 4 volume id + 47 2b b volume label + 52 36 8 file system type "FAT16 " / "FAT32 " / ... + +1b8 004 disk signature +1bc 002 null +1be 010 partition 0 +1ce 010 partition 1 +1de 010 partition 2 +1ee 010 partition 3 +1fe 002 mbr signature: 55 aa. + +Partition structure. Size: 10. + +0 1 bootable flag 00=no; 80=yes. +1 3 first sector in partition, chs +4 1 partition type +5 3 last sector in partition, chs +8 4 first sector in partition, lba +c 4 number of sectors in partition, for lba + +CHS address. Size: 3. +head = chs[0] +sector = chs[1] & 0x3f +cylinder = (chs[1] & 0xc0) << 2 | chs[2] + +Fat filesystem information sector. Size: 200. +000 4 signature: 52 52 61 41 +004 1e0 unused +1e4 4 fsinfo signature: 72 72 41 61 +1e8 4 number of free clusters +1ec 4 most recently allocated cluster +1f0 c reserved +1fc 2 unknown +1fe 2 boot record signature: 55 aa + +Directory entry. Size: 20. +00 8 name +08 3 extension +0b 1 attributes (00arshdv) +0c 1 0 +0d 1 creation time: (milli?)seconds +0e 2 creation time: hour and minute +10 2 creation date +12 2 last accessed date +14 2 cluster[31:16] +16 2 time +18 2 date +1a 2 cluster[15:0] +1c 4 file size + +long file name: stored in directory entries immediately before the 8.3 name (last entry is first part of name) +00 1 ordinal field, bit 6 set means highest. +01 a 5 unicode characters. +0b 1 attribute: 000rsh0v. +0c 1 0 +0d 1 checksum +0e c 6 unicode characters. +1a 2 0 +1c 4 2 unicode characters. + diff --git a/mips/arch.ccp b/mips/arch.ccp index 31518d4..eb95714 100644 --- a/mips/arch.ccp +++ b/mips/arch.ccp @@ -279,6 +279,15 @@ void arch_register_interrupt (unsigned num, kReceiver *r): arch_interrupt_receiver[num] = r // And enable or disable the interrupt. if r: + //if num != 0x18: + //kdebug ("enabled interrupt ") + //kdebug_num (num) + //kdebug (", state: ") + //kdebug_num (INTC_ISR) + //kdebug ("\n") intc_unmask_irq (num) else: + //kdebug ("disabled interrupt ") + //kdebug_num (num) + //kdebug ("\n") intc_mask_irq (num) diff --git a/mips/init.ccp b/mips/init.ccp index 5074187..700ceb1 100644 --- a/mips/init.ccp +++ b/mips/init.ccp @@ -57,6 +57,9 @@ static void init_idle (): idle_page.address_space = NULL current = &idle directory = idle_memory.arch.directory + kdebug ("idle thread is ") + kdebug_num ((unsigned)&idle) + kdebug ("\n") static void init_cp0 (): // Disable watchpoint interrupts. @@ -116,12 +119,14 @@ static void init_threads (): first_alarm = NULL kReceiver *init_receiver = NULL for unsigned i = 0; i < NUM_THREADS; ++i: - kdebug ("Starting thread ") - kdebug_num (i, 2) - kdebug ("\n") kMemory *mem = top_memory.alloc_memory () assert (mem) kThread *thread = mem->alloc_thread (NUM_SLOTS) + kdebug ("Starting thread ") + kdebug_num (i, 2) + kdebug (" at ") + kdebug_num ((unsigned)thread) + kdebug ("\n") #ifndef NDEBUG thread->id = i #endif diff --git a/mips/interrupts.ccp b/mips/interrupts.ccp index 1e40202..bbf9a04 100644 --- a/mips/interrupts.ccp +++ b/mips/interrupts.ccp @@ -111,6 +111,10 @@ kThread *interrupt (): // Handle timer interrupts specially: don't disable them. if i == TIMER_INTERRUPT: continue + //if i != 0x18: + //kdebug ("interrupt: ") + //kdebug_num (i, 2) + //kdebug ("\n") // Disable the interrupt while handling it. intc_mask_irq (i) intc_ack_irq (i) @@ -236,6 +240,9 @@ kThread *exception (): current->pc += 4 #endif #else + unsigned opcode = *(unsigned *)current->pc + if opcode == 0x0007000d: + panic (0, "Division by zero (detected by compiler)") current->pc += 4 if current->arch.a[0]: if dbg_cap.valid (): diff --git a/mips/nanonote/Makefile.arch b/mips/nanonote/Makefile.arch index e1bd759..bf69f36 100644 --- a/mips/nanonote/Makefile.arch +++ b/mips/nanonote/Makefile.arch @@ -16,6 +16,7 @@ # along with this program. If not, see . load = 0x80000000 +UDC_BOOT=1 ARCH_CXXFLAGS = -DNUM_THREADS=2 ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL @@ -28,8 +29,19 @@ LDFLAGS = --omagic -Ttext $(load) arch_iris_sources = mips/interrupts.cc mips/arch.cc boot_sources = mips/init.cc mips/nanonote/board.cc arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh -boot_threads = bootinit udc -programs = init gpio lcd bsquare ball buzzer metronome elfrun alarm gui nand sd+mmc +udc_boot_programs = udc +sd_boot_programs = sd+mmc partition fat +standard_boot_programs = bootinit +ifdef UDC_BOOT + boot_threads = $(standard_boot_programs) $(udc_boot_programs) + threadlist = mips/nanonote/threadlist-udc + INIT_FLAGS = -DUDC_BOOT +else + boot_threads = $(standard_boot_programs) $(sd_boot_programs) + threadlist = mips/nanonote/threadlist-sd + INIT_FLAGS = -DSD_BOOT +endif +programs = init gpio lcd bsquare ball buzzer metronome elfrun alarm gui nand test $(udc_boot_programs) $(sd_boot_programs) $(standard_boot_programs) all: test @@ -52,17 +64,16 @@ nanonote-boot: mips/nanonote/nanonote-boot.cc mips/nanonote/sdram-setup.raw mips/nanonote/sdram-setup.elf: mips/nanonote/sdram-setup.ld mips/nanonote/sdram-setup.elf: LDFLAGS = --omagic -T mips/nanonote/sdram-setup.ld -mips/nanonote/threadlist.o: $(addsuffix .elf,$(boot_threads)) +$(threadlist).o: $(addprefix fs/,$(addsuffix .elf,$(boot_threads))) mips/boot.o: TARGET_FLAGS = -DMEMORY_SIZE="32 << 20" -mips/init.o: TARGET_FLAGS = -I/usr/include -boot-programs/bootinit.o: TARGET_FLAGS = -I/usr/include +mips/init.o: TARGET_FLAGS = -I/usr/include $(INIT_FLAGS) +source/bootinit.o: TARGET_FLAGS = -I/usr/include source/elfrun.o: TARGET_FLAGS = -I/usr/include source/gpio.ccp: source/nanonote-gpio.ccp ln -s $(subst source/,,$<) $@ -$(addsuffix .elf,$(boot_threads)): TARGET_FLAGS = -I. -$(addsuffix .elf,$(boot_threads)): LDFLAGS = -EL +$(addprefix fs/,$(addsuffix .elf,$(boot_threads))): TARGET_FLAGS = -I. +$(addprefix fs/,$(addsuffix .elf,$(boot_threads))): LDFLAGS = -EL $(addprefix fs/,$(addsuffix .elf,$(programs))): LDFLAGS = -EL -$(addprefix boot-programs/,$(addsuffix .cc,$(boot_threads))): devices.hh keys.hh source/lcd.o: source/charset.data source/charset.data: source/charset @@ -72,7 +83,7 @@ source/charset.data: source/charset $(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@ # entry.o must be the first file. threadlist.o must be the first of the init objects (which can be freed after loading). -iris.elf: mips/entry.o $(subst .cc,.o,$(iris_sources)) mips/nanonote/threadlist.o mips/boot.o $(subst .cc,.o,$(boot_sources)) +iris.elf: mips/entry.o $(subst .cc,.o,$(iris_sources)) $(threadlist).o mips/boot.o $(subst .cc,.o,$(boot_sources)) $(LD) $(LDFLAGS) $^ -o $@ server: diff --git a/mips/nanonote/jz4740.hhp b/mips/nanonote/jz4740.hhp index 1355c94..b39495b 100644 --- a/mips/nanonote/jz4740.hhp +++ b/mips/nanonote/jz4740.hhp @@ -163,7 +163,6 @@ void cdelay (unsigned cs): #define IRQ_SSI 16 #define IRQ_CIM 17 #define IRQ_AIC 18 -#define IRQ_ETH 19 #define IRQ_DMAC 20 #define IRQ_TCU2 21 #define IRQ_TCU1 22 @@ -1227,8 +1226,6 @@ void cdelay (unsigned cs): #define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT) #define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) // 1-bit data bus #define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) // 4-bit data bus - #define CMDAT_BUS_WIDTH1 (0x0 << MSC_CMDAT_BUS_WIDTH_BIT) - #define CMDAT_BUS_WIDTH4 (0x2 << MSC_CMDAT_BUS_WIDTH_BIT) #define MSC_CMDAT_DMA_EN (1 << 8) #define MSC_CMDAT_INIT (1 << 7) #define MSC_CMDAT_BUSY (1 << 6) @@ -1245,13 +1242,14 @@ void cdelay (unsigned cs): #define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT) // Format R4 #define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT) // Format R5 #define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT) // Format R6 + #define MSC_CMDAT_RESPONSE_R7 (0x7 << MSC_CMDAT_RESPONSE_BIT) // Format R7 -#define CMDAT_DMA_EN (1 << 8) -#define CMDAT_INIT (1 << 7) -#define CMDAT_BUSY (1 << 6) -#define CMDAT_STREAM (1 << 5) -#define CMDAT_WRITE (1 << 4) -#define CMDAT_DATA_EN (1 << 3) +#define MSC_CMDAT_DMA_EN (1 << 8) +#define MSC_CMDAT_INIT (1 << 7) +#define MSC_CMDAT_BUSY (1 << 6) +#define MSC_CMDAT_STREAM (1 << 5) +#define MSC_CMDAT_WRITE (1 << 4) +#define MSC_CMDAT_DATA_EN (1 << 3) // MSC Interrupts Mask Register (MSC_IMASK) diff --git a/mips/nanonote/server/usb-server.ccp b/mips/nanonote/server/usb-server.ccp index ba4aafb..39408c7 100644 --- a/mips/nanonote/server/usb-server.ccp +++ b/mips/nanonote/server/usb-server.ccp @@ -156,7 +156,7 @@ void data::poll (): if !--lock: dir.clear () continue - case Iris::String::GET_PAGE: + case Iris::String::GET_BLOCK: if buffer[1] >= dir.size (): std::cerr << "reading invalid file" << std::endl usb_release_interface (handle, 0) diff --git a/mips/nanonote/threadlist-sd.S b/mips/nanonote/threadlist-sd.S new file mode 100644 index 0000000..04c343a --- /dev/null +++ b/mips/nanonote/threadlist-sd.S @@ -0,0 +1,49 @@ +// Iris: micro-kernel for a capability-based operating system. +// mips/nanonote/threadlist.S: List of initial threads. +// Copyright 2009 Bas Wijnen +// +// 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 . + + .globl init_start + .globl thread_start + .set noreorder + + .balign 0x1000 +thread0: + .incbin "fs/bootinit.elf" + + .balign 0x1000 +thread1: + .incbin "fs/sd+mmc.elf" + + .balign 0x1000 +thread2: + .incbin "fs/partition.elf" + + .balign 0x1000 +thread3: + .incbin "fs/fat.elf" + + .balign 0x1000 +thread4: + +// Everything from here may be freed after kernel initialization. +init_start: + +thread_start: + .word thread0 + .word thread1 + .word thread2 + .word thread3 + .word thread4 diff --git a/mips/nanonote/threadlist.S b/mips/nanonote/threadlist-udc.S similarity index 100% rename from mips/nanonote/threadlist.S rename to mips/nanonote/threadlist-udc.S diff --git a/screen.png b/screen.png new file mode 100644 index 0000000000000000000000000000000000000000..962f893ee43d586a86ecb3d5c7daa59bcb24fced GIT binary patch literal 45979 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 literal 0 HcmV?d00001 diff --git a/boot-programs/bootinit.ccp b/source/bootinit.ccp similarity index 99% rename from boot-programs/bootinit.ccp rename to source/bootinit.ccp index e369a3f..7da0691 100644 --- a/boot-programs/bootinit.ccp +++ b/source/bootinit.ccp @@ -138,7 +138,7 @@ static void run (Iris::String data, Iris::Memory parent_memory, Iris::Cap parent Iris::set_recv_arg (Iris::Cap (slot, p)) Iris::my_memory.create_page () Iris::Page (slot, p).set_flags (Iris::Page::PAYING, Iris::Page::PAYING) - data.get_page (p << PAGE_BITS, Iris::Cap (slot, p)) + data.get_block (p << PAGE_BITS, PAGE_SIZE, 0, Iris::Cap (slot, p)) Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS]) Iris::Thread thread = mem.create_thread (NUM_SLOTS) Elf32_Ehdr *header = (Elf32_Ehdr *)mapping diff --git a/source/crt0.ccp b/source/crt0.ccp index 7db7b5f..154f9a0 100644 --- a/source/crt0.ccp +++ b/source/crt0.ccp @@ -139,8 +139,9 @@ extern "C": Iris::Num ret = start () Iris::my_parent.exit (ret) Iris::my_memory.destroy (Iris::my_thread) - // The program no longer exists. If it somehow does, generate an address fault. + // The program no longer exists. If it somehow does, die again. while true: + Iris::panic (0, "this program should no longer exist.") *(volatile unsigned *)~0 __asm__ volatile ("\t.text\n" diff --git a/source/elfrun.ccp b/source/elfrun.ccp index fbb9eb4..099c7b5 100644 --- a/source/elfrun.ccp +++ b/source/elfrun.ccp @@ -83,7 +83,7 @@ static Iris::Caps map_string (Iris::String data): //kdebug_num (pages) //kdebug ("\n") Iris::set_recv_arg (Iris::Cap (slot, p)) - data.get_page (p << PAGE_BITS) + data.get_block (p << PAGE_BITS) Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS]) static Iris::Caps map_caps (Iris::Caps data, unsigned p): diff --git a/source/fat.ccp b/source/fat.ccp new file mode 100644 index 0000000..947c068 --- /dev/null +++ b/source/fat.ccp @@ -0,0 +1,550 @@ +#pypp 0 +#include +#include + +#define SECTOR_BITS 9 +#define BLOCK_MASK (~((1 << SECTOR_BITS) - 1)) +#define ROOT_CLUSTER 0x7fffffff + +static unsigned _free +extern unsigned _end + +void init_alloc (): + _free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK + +char *alloc_space (unsigned pages): + unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK + _free = ret + (pages << PAGE_BITS) + return (char *)ret + +void *operator new[] (unsigned size): + //kdebug ("new ") + void *ret = (void *)_free + size = (size + 3) & ~3 + unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1) + if rest < size: + unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS + for unsigned p = 0; p < pages; ++p: + Iris::Page page = Iris::my_memory.create_page () + page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME) + Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS)) + Iris::free_cap (page) + _free += size + //kdebug_num ((unsigned)ret) + //kdebug ("+") + //kdebug_num (size) + //kdebug ("\n") + return ret + +void *operator new (unsigned size): + return new char[size] + +static Iris::WString dev +static Iris::Num device_size +static Iris::Page page +static char *data +static Iris::Num current_block +static void read_block (Iris::Num idx, Iris::Page p = page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0): + if p.code == page.code: + if idx.value () == current_block.value () && offset == 0 && size == 1 << SECTOR_BITS: + return + current_block = idx + //kdebug ("fat getting block: ") + //kdebug_num (idx.h) + //kdebug (":") + //kdebug_num (idx.l) + //kdebug ("+") + //kdebug_num (size) + //kdebug ("@") + //kdebug_num (offset) + //kdebug ("\n") + dev.get_block (idx, size, offset, p) + +struct Fat: + char oem[8] + unsigned sector_size_bits + unsigned sectors_per_cluster_bits + unsigned reserved_sectors + unsigned num_fats + unsigned root_entries + unsigned sectors + unsigned media + unsigned sectors_per_fat + unsigned sectors_per_track + unsigned heads + unsigned hidden_sectors + unsigned active_fat + bool write_all_fats + unsigned fs_version + unsigned root_cluster + unsigned fsinfo_sector + unsigned boot_backup_sector + unsigned drive + unsigned current_head + unsigned volume_id + char label[0xb] + + unsigned bits + unsigned clusters + unsigned cluster_size_bits + unsigned root_sectors + unsigned header_sectors + unsigned bad_clusters + + unsigned free_clusters + unsigned last_alloced + + unsigned *fat + unsigned first_free_cluster, first_bad_cluster + + void print_num (char const *pre, unsigned data): + kdebug ("\t") + kdebug (pre) + unsigned bytes = 1 + while bytes < 8 && data >> (bytes * 4): + ++bytes + kdebug_num (data, bytes) + kdebug ("\n") + + void print_br (): + kdebug ("\tOEM: '") + for unsigned i = 0; i < 8; ++i: + kdebug_char (oem[i]) + kdebug ("'\n") + print_num ("bytes per sector: ", 1 << sector_size_bits) + print_num ("sectors per cluster: ", 1 << sectors_per_cluster_bits) + print_num ("reserved sectors: ", reserved_sectors) + print_num ("number of fats: ", num_fats) + print_num ("entries in root directory: ", root_entries) + print_num ("sectors: ", sectors) + print_num ("media descriptor: ", media) + print_num ("sectors per fat: ", sectors_per_fat) + print_num ("sectors per track: ", sectors_per_track) + print_num ("heads: ", heads) + print_num ("hidden sectors: ", hidden_sectors) + print_num ("active_fat: ", active_fat) + kdebug ("\twrite all: ") + kdebug (write_all_fats ? "yes\n" : "no\n") + print_num ("fs version: ", fs_version) + print_num ("root cluster: ", root_cluster) + print_num ("fsinfo sector: ", fsinfo_sector) + print_num ("boot sector backup sector: ", boot_backup_sector) + print_num ("drive: ", drive) + print_num ("current head: ", current_head) + print_num ("volume id: ", volume_id) + kdebug ("\tlabel: '") + for unsigned i = 0; i < 0xb; ++i: + kdebug_char (label[i]) + kdebug ("'\n") + print_num ("bits: ", bits) + print_num ("clusters: ", clusters) + print_num ("header sectors: ", header_sectors) + + unsigned read_num (char *data, unsigned bytes): + unsigned ret = 0 + for unsigned i = 0; i < bytes; ++i: + ret |= (data[i] & 0xff) << (i * 8) + return ret + + void map_fat_cluster (unsigned c, unsigned offset = 0): + read_block ((hidden_sectors + reserved_sectors + ((c * bits + 8 * offset) >> (sector_size_bits + 3))) << sector_size_bits) + + unsigned make_bits (unsigned orig): + unsigned ret + for ret = 0; ret < 32; ++ret: + if orig == 1 << ret: + return ret + Iris::panic (ret, "non-power of 2") + return ret + + void reset (): + read_block (0) + if data[0x1fe] != 0x55 || (data[0x1ff] & 0xff) != 0xaa: + kdebug ("invalid boot record signature in fat device\n") + for unsigned i = 0; i < 8; ++i: + oem[i] = data[3 + i] + sector_size_bits = make_bits (read_num (data + 0xb, 2)) + sectors_per_cluster_bits = make_bits (read_num (data + 0xd, 1)) + cluster_size_bits = sector_size_bits + sectors_per_cluster_bits + reserved_sectors = read_num (data + 0xe, 2) + num_fats = read_num (data + 0x10, 1) + root_entries = read_num (data + 0x11, 2) + sectors = read_num (data + 0x13, 2) + media = read_num (data + 0x15, 1) + sectors_per_fat = read_num (data + 0x16, 2) + sectors_per_track = read_num (data + 0x18, 2) + heads = read_num (data + 0x1a, 2) + hidden_sectors = read_num (data + 0x1c, 4) + if !sectors: + sectors = read_num (data + 0x20, 4) + if Iris::Num (sectors).value () << sector_size_bits > device_size.value (): + sectors = device_size.value () >> sector_size_bits + kdebug ("warning: limiting sectors because of limited device size\n") + + root_sectors = (root_entries * 32 + (1 << sector_size_bits) - 1) >> sector_size_bits + header_sectors = hidden_sectors + reserved_sectors + sectors_per_fat * num_fats + root_sectors + clusters = (sectors - header_sectors) >> sectors_per_cluster_bits + unsigned skip + if clusters >= 65525: + bits = 32 + sectors_per_fat = read_num (data + 0x24, 4) + active_fat = read_num (data + 0x28, 2) + write_all_fats = active_fat & 0x80 + active_fat &= 0xf + fs_version = read_num (data + 0x2a, 2) + root_cluster = read_num (data + 0x2c, 4) + fsinfo_sector = read_num (data + 0x30, 2) + boot_backup_sector = read_num (data + 0x32, 2) + skip = 0x40 - 0x24 + else: + if clusters < 4085: + bits = 12 + else: + bits = 16 + skip = 0 + active_fat = 0 + write_all_fats = true + fs_version = 0 + root_cluster = 0 + fsinfo_sector = 0 + boot_backup_sector = 0 + unsigned fat_entries_per_sector = (8 << sector_size_bits) / bits + unsigned fat_entries = sectors_per_fat * fat_entries_per_sector + if clusters + 2 > fat_entries: + clusters = fat_entries - 2 + kdebug ("warning: limiting clusters because of limited sector count\n") + drive = read_num (data + skip + 0x24, 1) + current_head = read_num (data + skip + 0x25, 1) + if data[skip + 0x26] == 0x29: + volume_id = read_num (data + skip + 0x27, 4) + for unsigned i = 0; i < 0xb; ++i: + label[i] = data[skip + 0x2b + i] + char *id = data + skip + 0x36 + if id[0] != 'F' || id[1] != 'A' || id[2] != 'T' || id[5] != ' ' || id[6] != ' ' || id[7] != ' ': + kdebug ("warning: file system type field was not 'FATxx '\n") + else: + switch bits: + case 12: + if id[3] != '1' || id[4] != '2': + kdebug ("warning: id for fat12 is not FAT12\n") + break + case 16: + if id[3] != '1' || id[4] != '6': + kdebug ("warning: id for fat16 is not FAT16\n") + break + case 32: + if id[3] != '3' || id[4] != '2': + kdebug ("warning: id for fat32 wat not FAT32") + break + else: + volume_id = 0 + for unsigned i = 0; i < 0xb; ++i: + label[i] = 0 + if fsinfo_sector: + read_block (fsinfo_sector << sector_size_bits) + if (data[0] & 0xff) != 0x52 || (data[1] & 0xff) != 0x52 || (data[2] & 0xff) != 0x6a || (data[3] & 0xff) != 0x41 || (data[0x1e4] & 0xff) != 0x72 || (data[0x1e5] & 0xff) != 0x72 || (data[0x1e6] & 0xff) != 0x4a || (data[0x1e7] & 0xff) != 0x61 || (data[0x1fe] & 0xff) != 0x55 || (data[0x1ff] & 0xff) != 0xaa: + kdebug ("invalid signature in fsinfo structure\n") + free_clusters = read_num (data + 0x1e8, 4) + last_alloced = read_num (data + 0x1ec, 4) + else: + free_clusters = ~0 + last_alloced = ~0 + + // Now read the FAT. + bad_clusters = 0 + fat = new unsigned[clusters] + unsigned counted_free_clusters = 0 + for unsigned c = 0; c < clusters; ++c: + // reduced cluster. + unsigned rc = c & (1 << sector_size_bits) - 1 + // The next line does nothing most of the time. + map_fat_cluster (c) + switch bits: + case 12: + fat[c] = data[(rc + 2) * 2] & 0xff + // There may be a sector boundary in the middle of the entry, so optionally reread. + map_fat_cluster (c, 1) + fat[c] |= (data[(rc + 2) * 2 + 1] & 0xff) << 8 + if c & 1: + fat[c] >>= 4 + else: + fat[c] &= 0xfff + break + case 16: + fat[c] = read_num (data + (rc + 2) * 2, 2) + break + case 32: + fat[c] = read_num (data + (rc + 2) * 4, 4) + break + // Correct for the crazy +2 offset, and keep a list of bad and free clusters. + if fat[c] == 0: + // Free cluster. + fat[c] = first_free_cluster + first_free_cluster = c + ++counted_free_clusters + else if fat[c] == 1: + // Invalid value. + Iris::panic (0, "entry is '1' in fat.") + else if bits == 12 && fat[c] == 0xfff || bits == 16 && fat[c] == 0xffff || bits == 32 && fat[c] == 0xfffffff: + // Last cluster in chain. + fat[c] = ~0 + else if bits == 12 && fat[c] == 0xff7 || bits == 16 && fat[c] == 0xfff7 || bits == 32 && fat[c] == 0xffffff7: + // Bad cluster. + fat[c] = first_bad_cluster + first_bad_cluster = c + ++bad_clusters + else: + // Non-last cluster in chain. + fat[c] -= 2 + unsigned fat_lookup (unsigned first_cluster, unsigned cluster): + while cluster--: + first_cluster = fat[first_cluster] + if first_cluster == ~0: + kdebug ("sector beyond end of file requested\n") + return ~0 + return first_cluster + struct File: + Fat *fat + unsigned size + unsigned first_cluster + char name[11] + bool archive, readonly, system, hidden, directory, volume + unsigned create_second, create_minute_hour, create_date, access_date, time, date + void load_cluster (unsigned idx, Iris::Page p = page, unsigned offset = 0): + unsigned cluster = fat->fat_lookup (first_cluster, idx >> fat->cluster_size_bits) + kdebug ("loading cluster ") + kdebug_num (idx) + kdebug ("@") + kdebug_num (cluster) + kdebug (" from file\n") + if cluster == ~0: + kdebug ("invalid cluster requested from file\n") + return + read_block ((fat->header_sectors + (Iris::Num (cluster).value () << fat->sectors_per_cluster_bits)) << fat->sector_size_bits, p, 1 << fat->cluster_size_bits, offset) + kdebug ("sector ") + kdebug_num (fat->header_sectors + (Iris::Num (cluster).value () << fat->sectors_per_cluster_bits)) + kdebug ("\n") + void get_dir_entry (unsigned dir, unsigned idx, File *f): + f->fat = this + unsigned sector = idx >> (sector_size_bits - 5) + unsigned num = (idx << 5) & ~BLOCK_MASK + Iris::Num hwsector + if dir == ROOT_CLUSTER: + if sector < root_sectors: + hwsector = header_sectors - root_sectors + sector + else: + hwsector = ~0 + else: + unsigned entry = fat_lookup (dir, sector) + if entry == ~0: + hwsector = ~0 + else: + hwsector = header_sectors + (Iris::Num (entry).value () << sectors_per_cluster_bits) + if hwsector.value () == ~0: + kdebug ("invalid sector requested from directory\n") + f->first_cluster = ~0 + return + read_block (hwsector.value () << sector_size_bits) + char *e = &data[num] + for unsigned i = 0; i < 11; ++i: + f->name[i] = e[i] + f->archive = e[0xb] & 0x20 + f->readonly = e[0xb] & 0x10 + f->system = e[0xb] & 0x8 + f->hidden = e[0xb] & 0x4 + f->directory = e[0xb] & 0x2 + f->volume = e[0xb] & 0x1 + f->create_second = read_num (e + 0xd, 1) + f->create_minute_hour = read_num (e + 0xe, 2) + f->create_date = read_num (e + 0x10, 2) + f->access_date = read_num (e + 0x12, 2) + f->time = read_num (e + 0x16, 1) + f->date = read_num (e + 0x18, 1) + f->size = read_num (e + 0x1c, 4) + f->first_cluster = (read_num (e + 0x14, 2) << 16 | read_num (e + 0x1a, 2)) - 2 + +// Capability encoding. +// 0:ROOT_CLUSTER = non fat-32 root directory. +// 0:cluster = other directory. +// cluster:index = file index from directory at cluster. +// cluster|0x80000000:index = filename for file with index from directory at cluster. + +Iris::Num start (): + init_alloc () + current_block = ~0 + dev = Iris::my_parent.get_capability () + if dev.get_align_bits () > SECTOR_BITS: + kdebug ("fat device doesn't support 512 byte access") + return 1 + device_size = dev.get_size () + data = (char *)0x15000; //alloc_space (1) + page = Iris::my_memory.create_page () + page.set_flags (Iris::Page::PAYING, Iris::Page::PAYING) + Iris::my_memory.map (page, (unsigned)data) + + Fat fat + + fat.reset () + fat.print_br () + + Iris::Cap root + if fat.root_cluster: + root = Iris::my_receiver.create_capability (Iris::Num (fat.root_cluster, 0)) + else: + root = Iris::my_receiver.create_capability (Iris::Num (ROOT_CLUSTER, 0)) + + Iris::my_parent.provide_capability (root.copy ()) + Iris::free_cap (root) + + while true: + Iris::wait () + unsigned dir = Iris::recv.protected_data.h + if dir & 0x80000000: + // File name. + unsigned idx = Iris::recv.protected_data.l + Iris::Cap reply = Iris::get_reply () + unsigned num = Iris::recv.data[1].l + unsigned size = Iris::recv.data[0].h >> 16 + unsigned cmd = Iris::recv.data[0].l + Fat::File f + fat.get_dir_entry (dir & ~0x80000000, idx, &f) + switch cmd: + case Iris::String::GET_SIZE: + kdebug ("filename size requested\n") + reply.invoke (11) + break + case Iris::String::GET_CHARS: + //kdebug ("filename chars requested\n") + /**/union { unsigned u[4]; char c[16]; } u + for unsigned k = 0; k < 4; ++k: + u.u[k] = 0 + for unsigned k = 0; k + num < 11; ++k: + u.c[k] = f.name[k + num] + reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3])) + break + case Iris::String::GET_ALIGN_BITS: + kdebug ("filename align requested\n") + reply.invoke (0) + break + case Iris::String::GET_BLOCK: + default: + Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename") + Iris::free_cap (reply) + else if dir: + // File. + unsigned idx = Iris::recv.protected_data.l + Iris::Cap reply = Iris::get_reply () + Iris::Cap arg = Iris::get_arg () + Iris::Num num = Iris::recv.data[1] + unsigned size = Iris::recv.data[0].h >> 16 + unsigned offset = Iris::recv.data[0].h & 0xffff + unsigned cmd = Iris::recv.data[0].l + Fat::File f + fat.get_dir_entry (dir, idx, &f) + switch cmd: + case Iris::String::GET_SIZE: + kdebug ("file size requested\n") + reply.invoke (f.size) + break + case Iris::String::GET_CHARS: + kdebug ("file chars requested\n") + unsigned mask = 1 << (fat.cluster_size_bits) - 1 + f.load_cluster (num.l & ~mask) + unsigned n = num.l & mask & ~0xf + unsigned *dat = (unsigned *)(data + n) + reply.invoke (Iris::Num (dat[0], dat[1]), Iris::Num (dat[2], dat[3])) + break + case Iris::String::GET_ALIGN_BITS: + kdebug ("file align requested\n") + reply.invoke (fat.cluster_size_bits) + break + case Iris::String::GET_BLOCK: + kdebug ("file block requested\n") + unsigned mask = 1 << (fat.cluster_size_bits) - 1 + if offset > PAGE_SIZE: + kdebug ("invalid offset requested\n") + break + if size + offset > PAGE_SIZE: + size = PAGE_SIZE - offset + for unsigned i = 0; i < size; i += 1 << fat.cluster_size_bits: + f.load_cluster ((num.l & ~mask) + i, arg, i + offset) + reply.invoke () + break + case Iris::WString::TRUNCATE: + case Iris::WString::SET_CHARS: + case Iris::WString::SET_BLOCK: + Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet") + default: + Iris::panic (Iris::recv.data[0].l, "invalid request for fat file") + Iris::free_cap (reply) + Iris::free_cap (arg) + else: + // Directory. + if Iris::recv.protected_data.l != ROOT_CLUSTER: + // Normal directory. + switch Iris::recv.data[0].l: + case Iris::Directory::GET_SIZE: + kdebug ("dir size requested\n") + Iris::recv.reply.invoke () + break + case Iris::Directory::GET_NAME: + kdebug ("dir name requested\n") + Iris::recv.reply.invoke () + break + case Iris::Directory::GET_FILE_RO: + kdebug ("dir file requested\n") + Iris::Cap reply = Iris::get_reply () + Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l)) + reply.invoke (0, 0, ret.copy ()) + Iris::free_cap (reply) + Iris::free_cap (ret) + break + case Iris::Directory::GET_FILE_INFO: + kdebug ("dir file info requested\n") + Iris::recv.reply.invoke () + break + case Iris::Directory::LOCK_RO: + case Iris::Directory::UNLOCK_RO: + kdebug ("dir lock or unlock requested\n") + Iris::recv.reply.invoke () + break + default: + kdebug ("invalid dir operation requested\n") + Iris::recv.reply.invoke () + break + else: + // Non-fat32 root directory. + switch Iris::recv.data[0].l: + case Iris::Directory::GET_SIZE: + kdebug ("root size requested\n") + Iris::recv.reply.invoke (fat.root_entries) + break + case Iris::Directory::GET_NAME: + //kdebug ("root name requested\n") + Iris::Cap reply = Iris::get_reply () + Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l | 0x80000000)) + reply.invoke (0, 0, ret.copy ()) + Iris::free_cap (reply) + Iris::free_cap (ret) + break + case Iris::Directory::GET_FILE_RO: + kdebug ("root file requested\n") + Iris::Cap reply = Iris::get_reply () + Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l)) + reply.invoke (0, 0, ret.copy ()) + Iris::free_cap (reply) + Iris::free_cap (ret) + break + case Iris::Directory::GET_FILE_INFO: + kdebug ("root file info requested\n") + Iris::recv.reply.invoke () + break + case Iris::Directory::LOCK_RO: + case Iris::Directory::UNLOCK_RO: + kdebug ("root lock or unlock requested\n") + Iris::recv.reply.invoke () + break + default: + kdebug ("invalid root operation requested\n") + Iris::recv.reply.invoke () + break diff --git a/source/init.ccp b/source/init.ccp index 91691af..9ef8ec7 100644 --- a/source/init.ccp +++ b/source/init.ccp @@ -182,7 +182,7 @@ static Iris::Caps load (char const *name, unsigned name_len, unsigned &size, Iri unsigned slot = target.use () for unsigned p = 0; p < pages; ++p: Iris::set_recv_arg (Iris::Cap (slot, p)) - file.get_page (p << PAGE_BITS) + file.get_block (p << PAGE_BITS) Iris::free_slot (slot) Iris::free_cap (file) root.unlock_ro () @@ -426,9 +426,9 @@ static void parse_line (char *&line, unsigned maxlen): Iris::panic (0, "capability given out twice") (*d)->dev->client = &**p ++(*p)->num_waiting - kdebug ("registered give device: ") - kdebug_num ((*d)->type) - kdebug ("\n") + //kdebug ("registered give device: ") + //kdebug_num ((*d)->type) + //kdebug ("\n") else if match (start, maxlen, "include"): unsigned name_len char *name = get_filename (line, maxlen, name_len) @@ -492,11 +492,11 @@ Iris::Num start (): if !d: Iris::panic (type, "unregistered device requested") Iris::recv.reply.invoke (0, 0, (*d)->dev->cap) - kdebug ("given device ") - kdebug_num (type) - kdebug (":") - kdebug_num (index) - kdebug ("\n") + //kdebug ("given device ") + //kdebug_num (type) + //kdebug (":") + //kdebug_num (index) + //kdebug ("\n") break case Iris::Parent::PROVIDE_CAPABILITY: if Iris::recv.data[1].h != 0: @@ -515,14 +515,14 @@ Iris::Num start (): if (*d)->client: if !--(*d)->client->num_waiting: (*d)->client->run () - kdebug ("provided ") - kdebug_num ((*d)->type) - kdebug (":") - kdebug_num ((*d)->index) - kdebug ("\n") + //kdebug ("provided ") + //kdebug_num ((*d)->type) + //kdebug (":") + //kdebug_num ((*d)->index) + //kdebug ("\n") break case Iris::Parent::INIT_DONE: - kdebug ("init done\n") + //kdebug ("init done\n") Iris::recv.reply.invoke () if caller == sysreq->server: Iris::Cap cap = Iris::my_receiver.create_capability (SYSREQ) @@ -530,6 +530,15 @@ Iris::Num start (): Iris::free_cap (cap) kdebug ("registered sysreq\n") break + case Iris::Parent::EXIT: + kdebug ("child exits with code ") + // TODO: print name. + kdebug_num (Iris::recv.data[1].h) + kdebug (":") + kdebug_num (Iris::recv.data[1].l) + kdebug ("\n") + // TODO: destroy memory. + break default: // TODO. kdebug ("child request: ") diff --git a/source/partition.ccp b/source/partition.ccp new file mode 100644 index 0000000..4485857 --- /dev/null +++ b/source/partition.ccp @@ -0,0 +1,129 @@ +#pypp 0 +#include +#include + +#define NUM_PARTITIONS 4 +#define SECTOR_BITS 9 +#define BLOCK_MASK (~((1 << SECTOR_BITS) - 1)) + +struct Partition: + static Iris::Num device_size + unsigned lba_start, lba_size + unsigned type + bool active + Iris::Num start, size + static unsigned read_num (char *data): + return data[0] & 0xff | (data[1] & 0xff) << 8 | (data[2] & 0xff) << 16 | (data[3] & 0xff) << 24 + void read (char *data): + if data[0] == 0: + active = false + else: + active = true + if (data[0] & 0xff) != 0x80: + kdebug ("Warning: invalid active code ") + kdebug_num (data[0], 2) + kdebug ("\n") + type = data[4] & 0xff + lba_start = read_num (data + 8) + lba_size = read_num (data + 12) + start = Iris::Num (lba_start).value () << SECTOR_BITS + size = Iris::Num (lba_size).value () << SECTOR_BITS + //kdebug ("Partition read: ") + //kdebug_num (lba_start) + //kdebug ("+") + //kdebug_num (lba_size) + //kdebug ("\n") + +Iris::Num Partition::device_size + +static Iris::WString dev +static void read_block (Iris::Num idx, Iris::Page page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0): + idx = idx.value () >> SECTOR_BITS + offset &= ~PAGE_MASK + if size + offset > PAGE_SIZE: + size = PAGE_SIZE - offset + size >>= SECTOR_BITS + for unsigned i = 0; i < size; ++i: + dev.get_block ((idx.value () + i) << SECTOR_BITS, 1 << SECTOR_BITS, (i << SECTOR_BITS) + offset, page) + +Iris::Num start (): + Partition::device_size = 0 + dev = Iris::my_parent.get_capability () + if dev.get_align_bits () > SECTOR_BITS: + kdebug ("partitioned device doesn't support 512 byte access") + return 1 + Partition::device_size = dev.get_size () + Iris::Page page = Iris::my_memory.create_page () + page.set_flags (Iris::Page::PAYING, Iris::Page::PAYING) + char *buffer = (char *)0x15000 + unsigned *ubuffer = (unsigned *)buffer + Iris::my_memory.map (page, (unsigned)buffer) + read_block (0, page) + + if buffer[0x1fe] != 0x55 || (buffer[0x1ff] & 0xff) != 0xaa: + kdebug ("invalid mbr signature\n") + + Partition partition[NUM_PARTITIONS] + + Iris::Cap cap + for unsigned i = 0; i < NUM_PARTITIONS; ++i: + partition[i].read (buffer + 0x1be + 0x10 * i) + cap = Iris::my_receiver.create_capability (i) + Iris::my_parent.provide_capability (cap.copy (), i) + Iris::free_cap (cap) + + page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME) + + Iris::my_parent.init_done () + + while true: + Iris::wait () + switch Iris::recv.data[0].l: + case Iris::String::GET_SIZE: + Iris::recv.reply.invoke (partition[Iris::recv.protected_data.l].size) + break + case Iris::String::GET_CHARS: + Iris::Cap reply = Iris::get_reply () + Iris::Num request = Iris::recv.data[1] + Iris::Num offset = (partition[Iris::recv.protected_data.l].start.value () + (request.value () & BLOCK_MASK)) & 0xf + unsigned page_offset = request.l & ~BLOCK_MASK + page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME) + read_block (offset, page) + reply.invoke (Iris::Num (ubuffer[page_offset >> 2 + 0], ubuffer[page_offset >> 2 + 1]), Iris::Num (ubuffer[page_offset >> 2 + 2], ubuffer[page_offset >> 2 + 3])) + page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME) + Iris::free_cap (reply) + break + case Iris::String::GET_ALIGN_BITS: + Iris::recv.reply.invoke (SECTOR_BITS) + break + case Iris::String::GET_BLOCK: + Iris::Cap reply = Iris::get_reply () + Iris::Cap arg = Iris::get_arg () + Iris::Num p = Iris::recv.data[1].value () & BLOCK_MASK + Iris::Num offset = partition[Iris::recv.protected_data.l].start.value () + unsigned size = Iris::recv.data[0].h >> 16 + unsigned out_offset = Iris::recv.data[0].h & 0xffff + //kdebug ("partition sending sector ") + //kdebug_num (offset.h) + //kdebug (":") + //kdebug_num (offset.l) + //kdebug (" + ") + //kdebug_num (p.h) + //kdebug (":") + //kdebug_num (p.l) + //kdebug (" = ") + //kdebug_num (Iris::Num (offset.value () + p.value ()).h) + //kdebug (":") + //kdebug_num (Iris::Num (offset.value () + p.value ()).l) + //kdebug ("\n") + read_block (offset.value () + p.value (), arg, size, out_offset) + reply.invoke () + Iris::free_cap (reply) + Iris::free_cap (arg) + break + case Iris::WString::SET_CHARS: + case Iris::WString::SET_BLOCK: + Iris::panic (Iris::recv.data[0].l, "writing to partitions not supported yet") + case Iris::WString::TRUNCATE: + default: + Iris::panic (Iris::recv.data[0].l, "invalid request for partition handler") diff --git a/source/sd+mmc.ccp b/source/sd+mmc.ccp index 9d9bc96..fd8a966 100644 --- a/source/sd+mmc.ccp +++ b/source/sd+mmc.ccp @@ -21,9 +21,37 @@ #include "arch.hh" class Mmc: - static unsigned const PORT = 3 - static unsigned const PIN = 2 - bool check_sdio () + public: + enum Response_type: + NONE = MSC_CMDAT_RESPONSE_NONE + DATA = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_DATA_EN + R1 = MSC_CMDAT_RESPONSE_R1 + R1B = MSC_CMDAT_RESPONSE_R1 | MSC_CMDAT_BUSY + R2 = MSC_CMDAT_RESPONSE_R2 + R3 = MSC_CMDAT_RESPONSE_R3 + R4 = MSC_CMDAT_RESPONSE_R4 + R5 = MSC_CMDAT_RESPONSE_R5 + R6 = MSC_CMDAT_RESPONSE_R6 + R7 = MSC_CMDAT_RESPONSE_R7 + static unsigned const POWER_PORT = 3 + static unsigned const POWER_PIN = 2 + struct CID: + unsigned mid + char oid[2] + char pnm[5] + unsigned prv + unsigned psn + unsigned year + unsigned month + struct CSD: + unsigned c_size + unsigned c_size_mult + unsigned read_bl_len, write_bl_len + bool copy + bool perm_write_protect + bool tmp_write_protect + bool send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response = NULL) + void check_sd () void check_sdmem () void check_mmc () public: @@ -31,23 +59,158 @@ class Mmc: void detect () void release () void interrupt () + CID const &get_cid (): + return cid + unsigned get_num_blocks (): + return num_blocks + unsigned get_read_block_size (): + return read_block_size + unsigned get_block_bits (): + return csd.read_bl_len > csd.write_bl_len ? csd.read_bl_len : csd.write_bl_len + void fill_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset) + private: + unsigned rca + bool have_sdmem, have_io + CID cid + CSD csd + unsigned num_blocks, read_block_size + Iris::Page buffer_page + static unsigned const buffer = 0x15000 + +bool Mmc::send (unsigned cmd, unsigned arg, Response_type response_type, unsigned *response): + MSC_CMD = cmd + MSC_ARG = arg + MSC_CMDAT = response_type + Iris::register_interrupt (IRQ_MSC) + msc_start_op () + Iris::wait_for_interrupt (IRQ_MSC) + //kdebug ("cmd: ") + //kdebug_num (cmd) + unsigned stat = MSC_STAT + //kdebug (", stat: ") + //kdebug_num (stat) + //kdebug ("\n") + if stat & MSC_STAT_CRC_RES_ERR: + Iris::panic (0, "crc error in mmc response") + return false + if stat & MSC_STAT_TIME_OUT_RES: + //kdebug ("time out waiting for mmc response\n") + return false + if response_type == R2: + unsigned d = MSC_RES + if d >> 8 != 0x3f: + Iris::panic (d, "invalid r2 response") + if cmd == 3: + // Read out result. + cid.mid = d & 0xff + d = MSC_RES + cid.oid[0] = d >> 8 + cid.oid[1] = d & 0xff + d = MSC_RES + cid.pnm[0] = d >> 8 + cid.pnm[1] = d & 0xff + d = MSC_RES + cid.pnm[2] = d >> 8 + cid.pnm[3] = d & 0xff + d = MSC_RES + cid.pnm[4] = d >> 8 + cid.prv = d & 0xff + d = MSC_RES + cid.psn = d << 16 + d = MSC_RES + cid.psn |= d + d = MSC_RES + cid.year = 2000 + (d >> 4 & 0xff) + cid.month = d & 0xf + else: + // Header (8) 1.0 1.0 + // Read out csd. + // Ignore csd_structure. 2 (+ 6) 1.0 2.0 *** + d = MSC_RES + // Ignore taac and nsac. 8 + 8 2.0 4.0 *** + d = MSC_RES + // Ignore tran_speed, ccc. 8 + 8/12 2.0 6.0 *** + d = MSC_RES + // Ignore rest of ccc. 4/12 0.4 6.4 + // 4 0.4 7.0 + csd.read_bl_len = (d >> 8) & 0xf + // Ignore read_bl_partial, write_blk_misalign, read_blk_misalign, dsr_imp. 1 + 1 + 1 + 1 (+ 2) 0.6 7.6 + // 2/12 0.2 8.0 *** + csd.c_size = (d & 0x0003) << 10 + d = MSC_RES + // 10/12 1.2 9.2 + csd.c_size |= d >> 6 + // Ignore vdd_r_cur_min, vdd_r_cur_max. 3 + 3 0.6 10.0 *** + d = MSC_RES + // Ignore vdd_w_cur_min, vdd_w_cur_max. 3 + 3 0.6 10.6 + // 3 0.3 11.1 + csd.c_size_mult = (d >> 7) & 0x7 + // Ignore erase_blk_enable, sector_size. 1 + 6/7 0.7 12.0 *** + d = MSC_RES + // Ignore rest of sector_size, wp_grp_size, wp_grp_enable, r2w_factor. 1/7 + 7 + 1 (+ 2) + 3 1.6 13.6 + // 2/4 0.4 14.0 *** + csd.write_bl_len = (d << 2) & 0xc + d = MSC_RES + // 2/4 0.2 14.2 + csd.write_bl_len |= (d >> 14) & 0x3 + // Ignore write_bl_partial, file_format_grp. 1 (+ 5) + 1 0.7 15.1 + // 1 0.1 15.2 + csd.copy = d & 0x40 + // 1 0.1 15.3 + csd.perm_write_protect = d & 0x20 + // 1 0.1 15.4 + csd.tmp_write_protect = d & 0x10 + // Ignore file_format. 2 (+ 2) 0.4 16.0 *** + read_block_size = 1 << csd.read_bl_len + num_blocks = (csd.c_size + 1) << (csd.c_size_mult + 2) + else if response_type != NONE: + unsigned r = MSC_RES + if response_type == R3: + if r >> 8 != 0x3f: + Iris::panic (r, "r3 response was not 3f") + else if r >> 8 != cmd: + Iris::panic (r, "response doesn't match command") + r <<= 24 + r |= MSC_RES << 8 + r |= MSC_RES & 0xff + if response: + *response = r + //kdebug ("extra response fifo read: ") + //for unsigned i = 0; i < 9; ++i: + //kdebug (" ") + //kdebug_num (MSC_RES, 4) + //kdebug ("\n") + MSC_IREG = MSC_IREG_END_CMD_RES + return true void Mmc::reset (): + // Create a buffer to use for data transfer. + buffer_page = Iris::my_memory.create_page () + Iris::my_memory.map (buffer_page, buffer) + // Reset all state, by faking a release event. + release () // Enable slow clock to msc. CPM_MSCCDR = ~0 cpm_start_msc () // Enable msc pins. gpio_as_msc () // Disable power to card. - gpio_as_gpio (PORT, 1 << PIN) - gpio_as_output (PORT, 1 << PIN) - gpio_disable_pull (PORT, 1 << PIN) - gpio_set (PORT, 1 << PIN) + gpio_as_gpio (POWER_PORT, 1 << POWER_PIN) + gpio_as_output (POWER_PORT, 1 << POWER_PIN) + gpio_disable_pull (POWER_PORT, 1 << POWER_PIN) + gpio_set (POWER_PORT, 1 << POWER_PIN) // Stop the clock. MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP while MSC_STAT & MSC_STAT_CLK_EN: - Iris::schedule () + //kdebug (",") + Iris::sleep (1) + + // Reset controller and inserted devices. + MSC_STRPCL = MSC_STRPCL_RESET + while MSC_STAT & MSC_STAT_IS_RESETTING: + //kdebug (":") + Iris::sleep (1) // Initialize registers. MSC_CLKRT = MSC_CLKRT_CLK_RATE_DIV_128 @@ -55,82 +218,166 @@ void Mmc::reset (): MSC_RDTO = ~0 MSC_BLKLEN = 0x200 MSC_NOB = 0 - MSC_IMASK = ~0 + MSC_IREG = ~0 + MSC_IMASK = ~(MSC_IMASK_END_CMD_RES | MSC_IMASK_RXFIFO_RD_REQ | MSC_IMASK_TXFIFO_WR_REQ) MSC_ARG = 0 - // Reset controller and inserted devices. - MSC_STRPCL = MSC_STRPCL_RESET - while MSC_STAT & MSC_STAT_IS_RESETTING: - Iris::schedule () - // Start the clock. MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START // Set cards, if any, to idle. MSC_CMD = 0 MSC_CMDAT = MSC_CMDAT_RESPONSE_NONE + Iris::register_interrupt (IRQ_MSC) msc_start_op () - while !msc_ireg_end_cmd_res (): - Iris::schedule () + Iris::wait_for_interrupt (IRQ_MSC) msc_ireg_clear_end_cmd_res () // Reset SDIO device, if any. MSC_CMD = 52 MSC_ARG = 0x88000C08 MSC_CMDAT = MSC_CMDAT_RESPONSE_R5 + Iris::register_interrupt (IRQ_MSC) msc_start_op () - while !msc_ireg_end_cmd_res (): - Iris::schedule () + Iris::wait_for_interrupt (IRQ_MSC) msc_ireg_clear_end_cmd_res () -bool Mmc::check_sdio (): - // 2. Send CMD5 (IO_SEND_OP_CMD) to validate voltage. - // 3. If the response is correct and the number of IO functions > 0, then continue, else go to check SDMEM. - // 4. If C-bit in the response is ready (the initialization has finished), go to 6. - // 5. Send CMD5 (IO_SEND_OP_CMD) to validate voltage, then go to 4. - // 6. If memory-present-bit in the response is true, then it is a combo card (SDIO + Memory), else - // it is only a SDIO card. - // 7. If it is a combo card, go to check SDMEM to initialize the memory part. - // 8. Send CMD3 (SET_RELATIVE_ADDR) to let the card publish a RCA. The RCA is returned - // from the response. - // 9. If do not accept the new RCA, go to 8, else record the new RCA. - // 10. Go to check MMC, because we can assure that there is no SDMEM card. - return false - -void Mmc::check_sdmem (): - // 2. Send CMD55. Here the default RCA 0x0000 is used for CMD55. - // 3. If the response is correct (CMD55 has response), then continue, else go to check MMC. - // 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000). - // 5. If the initialization has finished, go to 7. (The response is the OCR register and it includes a status information bit (bit [31]). This status bit is set if the card power up procedure has been finished. As long as the card is busy, the corresponding bit[31] is set to LOW.) - // 6. Send CMD55 and ACMD41 to validate voltage, and then go to 5. - // 7. Send CMD2 (ALL_SEND_CID) to get the card CID. - // 8. Send CMD3 (SET_RELATIVE_ADDR) to let card publish a RCA. The RCA is returned from the response. - // 9. If do not accept the new RCA, go to 8, else record the new RCA. - // 10. Go to check MMC. - void Mmc::check_mmc (): + //kdebug ("checking mmc\n") // 1. SEND CMD1 (SEND_OP_CMD) TO VALIDATE VOLTAGE (THE GENERAL OCR VALUE IS 0X00FF88000). // 2. IF THE RESPONSE IS CORRECT, THEN CONTINUE, ELSE GOTO 9. - // 3. IF THE INITIALIZATION HAS FINISHED, GO TO 5. (THE RESPONSE IS THE OCR REGISTER AND IT INCLUDES A STATUS INFORMATION BIT (BIT [31]). THIS STATUS BIT IS SET IF THE CARD POWER UP PROCEDURE HAS BEEN FINISHED. AS LONG AS THE CARD IS BUSY, THE CORRESPONDING BIT[31] IS SET TO LOW.) - // 4. Send CMD1 (SEND_OP_CMD) to validate voltage, and then go to 3. - // 5. Send CMD2 (ALL_SEND_CID) to get the card CID. - // 6. If the response timeout occurs, goto 9. - // 7. Send CMD3 (SET_RELATIVE_ADDR) to assign the card a RCA. - // 8. If there are other MMC cards, then go to 5. + // 3. IF THE INITIALIZATION HAS FINISHED, GO TO 5. (THE RESPONSE IS THE OCR REGISTER AND IT INCLUDES A STATUS INFORMATION BIT (BIT [31]). THIS STATUS BIT IS SET IF THE CARD POWER UP PROCEDURE HAS BEEN FINISHED. AS LONG AS THE CARD IS BUSY, THE CORRESPONDING BIT[31] IS SET TO LOW.) + // 4. Send CMD1 (SEND_OP_CMD) to validate voltage, and then go to 3. + // 5. Send CMD2 (ALL_SEND_CID) to get the card CID. + // 6. If the response timeout occurs, goto 9. + // 7. Send CMD3 (SET_RELATIVE_ADDR) to assign the card a RCA. + +void Mmc::check_sdmem (): + kdebug ("checking sdmem\n") + send (0, 0, NONE) + // 2. Send CMD55. Here the default RCA 0x0000 is used for CMD55. + // 3. If the response is correct (CMD55 has response), then continue, else go to check MMC. + unsigned code + bool hc = false + if send (8, 0x1aa, R7, &code) && (code & 0xff) == 0xaa: + kdebug ("hc\n") + hc = true + if !send (55, 0, R1, &code): + check_mmc () + return + // 4. Send ACMD41 (SD_SEND_OP_CMD) to validate voltage (the general OCR value is 0x00FF8000). + if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code): + check_mmc () + return + // 5. If the initialization has finished, go to 7. (The response is the OCR register and it includes a status information bit (bit [31]). This status bit is set if the card power up procedure has been finished. As long as the card is busy, the corresponding bit[31] is set to LOW.) + // 6. Send CMD55 and ACMD41 to validate voltage, and then go to 5. + unsigned retries = 100 + while !(code & (1 << 31)) && --retries: + if !send (55, 0, R1, &code): + return + if !send (41, hc ? 0x40800000 : 0x00800000, R3, &code): + return + Iris::sleep (1) + if !(code & (1 << 31)): + Iris::panic (code, "card fails to finish setting up") + // 7. Send CMD2 (ALL_SEND_CID) to get the card CID. + if !send (2, 0, R2): + Iris::panic (0, "card failed to send CID") + // 8. Send CMD3 (SET_RELATIVE_ADDR) to let card publish a RCA. The RCA is returned from the response. + // 9. If do not accept the new RCA, go to 8, else record the new RCA. + rca = 0 + while !rca: + if !send (3, 0, R6, &rca): + Iris::panic (0, "card failed to provide rca") + rca &= 0xffff0000 + kdebug ("received rca ") + kdebug_num (rca >> 16, 4) + kdebug ("\n") + have_sdmem = true + +void Mmc::check_sd (): + //kdebug ("checking sdio\n") + if !send (0, 0, NONE): + Iris::panic (0, "unable to reset cards?") + // 2. Send CMD5 (IO_SEND_OP_CMD) to validate voltage. + // 3. If the response is correct and the number of IO functions > 0, then continue, else go to check SDMEM. + unsigned code + if !send (5, 1 << 20, R4, &code) || !(code & (7 << 28)): + check_sdmem () + return + // 4. If C-bit in the response is ready (the initialization has finished), go to 6. + // 5. Send CMD5 (IO_SEND_OP_CMD) to validate voltage, then go to 4. + while !(code & (1 << 31)): + if !send (5, 1 << 20, R4, &code): + Iris::panic (0, "invalid response to cmd 5") + // 6. If memory-present-bit in the response is true, then it is a combo card (SDIO + Memory), else it is only a SDIO card. + // 7. If it is a combo card, go to check SDMEM to initialize the memory part. + have_io = true + if code & (1 << 27): + check_sdmem () + return + // 8. Send CMD3 (SET_RELATIVE_ADDR) to let the card publish a RCA. The RCA is returned from the response. + // 9. If do not accept the new RCA, go to 8, else record the new RCA. + rca = 0 + while rca == 0: + if !send (3, 0, R6, &rca): + Iris::panic (0, "unable to set rca") + rca &= 0xffff0000 + check_mmc () void Mmc::detect (): kdebug ("mmc detect\n") - gpio_clear (PORT, 1 << PIN) - if check_sdio (): - check_sdmem () + gpio_clear (POWER_PORT, 1 << POWER_PIN) + check_sd () check_mmc () + if have_sdmem: + if !send (9, rca, R2): + Iris::panic (0, "unable to request csd") + if !send (7, rca, R1B): + Iris::panic (0, "unable to select sdmem") + kdebug ("found device; size = ") + kdebug_num (num_blocks) + kdebug (" * ") + kdebug_num (read_block_size) + kdebug (" = ") + kdebug_num (num_blocks * read_block_size) + kdebug ("\n") void Mmc::release (): kdebug ("mmc release\n") - gpio_set (PORT, 1 << PIN) + gpio_set (POWER_PORT, 1 << POWER_PIN) + have_sdmem = false + have_io = false + read_block_size = 0 + num_blocks = 0 void Mmc::interrupt (): kdebug ("mmc interrupt\n") +void Mmc::fill_page (Iris::Page page, Iris::Num address, unsigned size, unsigned offset): + if address.h: + Iris::panic (0, "page too high: not supported") + return + unsigned blockmask = ~((1 << get_block_bits ()) - 1) + unsigned p = address.l & blockmask + size &= blockmask + offset &= ~PAGE_MASK + if size + offset > PAGE_SIZE: + size = PAGE_SIZE - offset + page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME) + page.share (buffer_page) + buffer_page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME, Iris::Page::PAYING | Iris::Page::FRAME) + MSC_NOB = 1 + MSC_BLKLEN = read_block_size + for unsigned a = 0; a < size; a += 1 << get_block_bits (): + if !send (17, p + a, DATA): + Iris::panic (0, "unable to request data") + for unsigned aa = 0; aa < read_block_size; aa += 4: + Iris::register_interrupt (IRQ_MSC) + Iris::wait_for_interrupt (IRQ_MSC) + *(unsigned *)(buffer + a + aa + offset) = MSC_RXFIFO + MSC_IREG = MSC_IREG_DATA_TRAN_DONE + +static Mmc mmc enum types: DETECT @@ -141,7 +388,6 @@ Iris::Num start (): map_gpio () map_cpm () - Mmc mmc mmc.reset () Iris::Event detect = Iris::my_parent.get_capability () @@ -178,7 +424,34 @@ Iris::Num start (): mmc.interrupt () break case REQUEST: - kdebug ("sd+mmc request\n") + //kdebug ("sd+mmc request ") + //kdebug_num (Iris::recv.data[0].l) + //kdebug ("\n") + switch Iris::recv.data[0].l: + case Iris::String::GET_SIZE: + unsigned long long size = mmc.get_num_blocks () * mmc.get_read_block_size () + Iris::recv.reply.invoke (size) + break + case Iris::String::GET_CHARS: + Iris::panic (0, "get chars from mmc not supported yet") + break + case Iris::String::GET_ALIGN_BITS: + Iris::recv.reply.invoke (mmc.get_block_bits ()) + break + case Iris::String::GET_BLOCK: + Iris::Cap reply = Iris::get_reply () + Iris::Page page = Iris::get_arg () + mmc.fill_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff) + reply.invoke () + Iris::free_cap (page) + Iris::free_cap (reply) + break + case Iris::WString::SET_CHARS: + case Iris::WString::SET_BLOCK: + // Fall through: don't support writing yet. + case Iris::WString::TRUNCATE: + default: + Iris::panic (0, "unexpected event for sd+mmc") break default: Iris::panic (0, "unexpected request source for sd+mmc") diff --git a/source/test.ccp b/source/test.ccp new file mode 100644 index 0000000..8fb94a0 --- /dev/null +++ b/source/test.ccp @@ -0,0 +1,60 @@ +#pypp 0 +// Iris: micro-kernel for a capability-based operating system. +// source/test.ccp: Testing program. +// Copyright 2009 Bas Wijnen +// +// 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 + +bool match (char const *a, char const *b): + for unsigned i = 0; i < 11; ++i: + if a[i] != b[i]: + return false + return true + +Iris::Num start (): + Iris::Directory dir = Iris::my_parent.get_capability () + dir.lock_ro () + Iris::Num files = dir.get_size () + for Iris::Num i = 0; i.value () < files.value (); i = i.value () + 1: + Iris::String f = dir.get_name (i) + char start[16] + f.get_chars (0, start) + if match (start, "TEST TXT"): + Iris::free_cap (f) + f = dir.get_file_ro (i) + dir.unlock_ro () + Iris::Page p = f.get_block (0) + char *mapping = (char *)0x15000 + Iris::my_memory.map (p, (unsigned)mapping) + for unsigned j = 0; j < PAGE_SIZE; ++j: + kdebug_char (mapping[j]) + kdebug_char ('\n') + return 0 + unsigned i + for i = 0; i < 16; ++i: + if start[i] != 0: + break + if i < 16: + for i = 0; i < 16; ++i: + kdebug_num (start[i], 2) + kdebug (" ") + for i = 0; i < 16; ++i: + kdebug_char (start[i]) + kdebug ("\n") + Iris::free_cap (f) + kdebug ("file test.txt not found\n") + return 1 diff --git a/boot-programs/udc.ccp b/source/udc.ccp similarity index 98% rename from boot-programs/udc.ccp rename to source/udc.ccp index 1d69c7f..9f9e981 100644 --- a/boot-programs/udc.ccp +++ b/source/udc.ccp @@ -600,7 +600,7 @@ Iris::Num start (): Iris::free_cap (reply) Iris::free_cap (arg) continue - case Iris::String::GET_PAGE: + case Iris::String::GET_BLOCK: default: reply.invoke (Iris::ERR_INVALID_OPERATION) Iris::free_cap (reply) @@ -669,9 +669,12 @@ Iris::Num start (): case FILE: //kdebug ("file request\n") switch Iris::recv.data[0].l: + case Iris::String::GET_BLOCK: + if Iris::recv.data[0].h != PAGE_SIZE << 16: + Iris::panic (0, "unsupported get_block arguments for boot usb device driver") + // Fall through. case Iris::String::GET_SIZE: case Iris::String::GET_CHARS: - case Iris::String::GET_PAGE: udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg) continue default: