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 0000000..962f893 Binary files /dev/null and b/screen.png differ 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: